两周前,项目里需要实现一个红心飘飘的点赞效果。抓耳挠腮了老半天,看了几篇大佬的文章,终于算是摸了个七七八八。不禁长叹一声,还是菜啊。先来看一下效果:(传送门进去点一波)

一、Bezier曲线运动轨迹

其实用大白话描述一下需求就是让一个红心图片沿着贝塞尔曲线的轨迹走,然后边走边消失。核心在于得到贝塞尔曲线上的一系列点。本文不会讲解贝塞尔曲线的原理,因为大佬们已经讲过了,而且讲的比我好。参考文章如下:

其中第二篇文章讲到了生成二阶和三阶贝塞尔曲线可以使用canvas自带的方法:quadraticCurveTobezierCurveTo,而高阶的则先得到曲线上一系列的点,然后顺次连接这些点来拟合高阶的贝塞尔曲线。没错,我们要的就是这一系列的点,有了这些点,就可以控制红心的轨迹了。下面是我基于作者的BezierMarker.js写的一个demo,可以直观地看出高阶贝塞尔曲线上的点:

See the Pen XyWZXj by imgss (@imgss) on CodePen.

上面100个曲线上的点坐标是由下面这段代码计算得出的:

BezierMaker.prototype.bezier = function(t) { //贝塞尔公式调用
var x = 0,
y = 0,
bezierCtrlNodesArr = this.bezierCtrlNodesArr,
n = bezierCtrlNodesArr.length - 1,
self = this
bezierCtrlNodesArr.forEach(function(item, index) {
if(!index) {
x += item.x * Math.pow(( 1 - t ), n - index) * Math.pow(t, index)
y += item.y * Math.pow(( 1 - t ), n - index) * Math.pow(t, index)
} else {
x += self.factorial(n) / self.factorial(index) / self.factorial(n - index) * item.x * Math.pow(( 1 - t ), n - index) * Math.pow(t, index)
y += self.factorial(n) / self.factorial(index) / self.factorial(n - index) * item.y * Math.pow(( 1 - t ), n - index) * Math.pow(t, index)
}
})
return {
x: x,
y: y
}
}

这个方法就是对贝塞尔公式的实现。以3阶贝塞尔公式为例(见下图),它的方程需要四个控制点(P1,P2,P3,P4)和一个t值,就能计算出曲线上的某一点的坐标。


根据给定的`t`值,结合控制点的坐标,算出相应`t`值下的贝塞尔曲线上的点的坐标。拿下图(来自第一篇文章)来说,给定`t`值为0.25,就可以得到B点的坐标

当将t由0递增到1时,就可以得到100个曲线上的点,进而拟合出相应的曲线。当我们拿到这一系列点时,其实问题已经解决了一大半了。

二、使红心飘起来

拿到拟合点数组后,绘制轨迹就是从数组中依次拿出坐标,并将红心图片绘制到相应的坐标上。并根据当前拟合点在曲线数组中的位置,改变图片的不透明度,就可以让红心飘起来了,上一部分代码,讲解见注释:

// 生成随机数
function rnd () {
let flag = Math.random() > 0.5 ? 1 : -1
return 80 * Math.random() * flag
} class FlyHeart {
constructor (ctx, img) {
this.ctx = ctx;
this.img = heart;
// 拿到红心的运动轨迹,一系列拟合点坐标
this.bezierArr = new BezierMaker(ctx, [
{x: 187, y: 245},
{x: 170 + rnd(), y: 200},
{x: 200 + rnd() , y: 120},
{x: 140 + rnd(), y: 60}], 90).bezierArr //90表示拟合点的数量,rnd使红心的轨迹有一定的随机性
}
draw () {
// 依次取出轨迹的每个点
let position = this.bezierArr.shift(); // 清除上次画的
this.clear(); if (position) {
this.ctx.save()
// 根据当前数组长度算出透明度
this.ctx.globalAlpha = this.bezierArr.length / 30;
this.ctx.drawImage(this.img, position.x , position.y, 20, 20);
this.ctx.restore();
this.prevPosition = position;
}
}
// 清除上次画的
clear () {
if (this.prevPosition) {
this.ctx.clearRect(this.prevPosition.x, this.prevPosition.y, 20, 20);
}
}
}

接下来就是给body添加点击事件,当点击时,就新生成一个红心:

  document.body.addEventListener('click', function() {
heartArr.push(new FlyHeart(ctx, heart));
}) let heartArr = []
const cvs = document.getElementById('cvs')
const ctx = cvs.getContext('2d')
const heart = document.getElementById('heart') //图片 function draw () {
if(heartArr.length) {
for(let heart of heartArr) {
heart.draw();
if(heart.bezierArr.length === 0) {
heart.clear();
let index = heartArr.indexOf(heart)
heartArr.splice(index, 1)
}
}
}
requestAnimationFrame(draw)
}
draw()

三、后记

当时看到这个需求的时候,真的是一筹莫展,看到n阶贝塞尔曲线时更是一头雾水,但是看不懂也要看,然后看着看着,看多了也就慢慢明白了。希望没浪费大家的时间,各位看官看完后有所收获(完)

用canvas实现红心飘飘的动画效果的更多相关文章

  1. 多种方法实现Loading(加载)动画效果

    当我们ajax提交一个按钮的时候,给那个按钮来个Loading效果会高端很多,体验也会上升个层次. 既能让用户知道正在提交中,也能防止二次提交,好处多多呢.

  2. (转)多种方法实现Loading(加载)动画效果

    当我们ajax提交一个按钮的时候,给那个按钮来个Loading效果会高端很多,体验也会上升个层次. 既能让用户知道正在提交中,也能防止二次提交,好处多多呢.

  3. html5 canvas首屏自适应背景动画循环效果代码

    模板描述:html5 canvas首屏自适应背景动画循环效果代码 由于动态图太大,怕以后服务器受不了,所以现在都改为静态图了,大家点击演示地址一样的,希望大家喜欢,你们的支持就是小海的动力!! 欢迎大 ...

  4. 基于canvas实现物理运动效果与动画效果(一)

    一.为什么要写这篇文章 某年某月某时某种原因,我在慕课网上看到了一个大神实现了关于小球的抛物线运动的代码,心中很是欣喜,故而写这篇文章来向这位大神致敬,同时也为了弥补自己在运动效果和动画效果制作方面的 ...

  5. 7个惊艳的HTML5 Canvas动画效果及源码

    HTML5非常强大,尤其是现在大部分浏览器都支持HTML5和CSS3,用HTML5制作的动画也多了起来.另外,Canvas上绘制图形非常简单,本文就分享了一些强大的HTML5 Cnavas动画,一起来 ...

  6. HTML5 Canvas动画效果演示

    HTML5 Canvas动画效果演示 主要思想: 首先要准备一张有连续帧的图片,然后利用HTML5 Canvas的draw方法在不同的时间 间隔绘制不同的帧,这样看起来就像动画在播放. 关键技术点: ...

  7. 使用Canvas实现动画效果 | DKlogs -- 设计 | 生活

    使用Canvas实现动画效果 | DKlogs -- 设计 | 生活 使用Canvas实现动画效果

  8. HTML5 Canvas动画效果演示 - 流浪的鱼 - 博客频道 - CSDN.NET

    HTML5 Canvas动画效果演示 - 流浪的鱼 - 博客频道 - CSDN.NET HTML5 Canvas动画效果演示

  9. HTML5 Canvas动画效果实现原理

    在线演示 使用HTML5画布可以帮助我们高速实现简单的动画效果.基本原理例如以下: 每隔一定时间绘制图形而且清除图形,用来模拟出一个动画过程,能够使用context.clearRect(0, 0, x ...

随机推荐

  1. 【转】【完全开源】百度地图Web service API C#.NET版,带地图显示控件、导航控件、POI查找控件

    [转][完全开源]百度地图Web service API C#.NET版,带地图显示控件.导航控件.POI查找控件 目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET ...

  2. 2019-1-24 Spark 学习 --总体架构

    2019-1-24 Spark 学习 --总体架构 新建 模板 小书匠 1548339392539.jpg 1548339357270.jpg 1548339372461.jpg 1548339345 ...

  3. sequence测试中的使用

    1. create sequence : create sequence TEST_SEQUENCE minvalue 1 maxvalue 1000000000 start with 1 incre ...

  4. loadrunner整体压测执行操作步骤

    lr11安装包链接:https://pan.baidu.com/s/1hF3j2Vi_xB8BhT70P1ZdBg 提取码:n3zn lr12安装包链接:https://pan.baidu.com/s ...

  5. Effective Java -- 使可变性最小化

    为了使类成为不可变的,应该遵循以下五条原则: 1. 不要提供任何会下盖对象状态的方法 2. 保证类不会被扩展 3. 使所有的域都是final的 4. 使所有的域都成为私有的 5. 确保对于任何可变组件 ...

  6. DOS命令 学习笔记

    将遇到的一些DOS命令(linux命令和DOS命令都存在的命令也记录在此处)记录下,方便以后查询 DOS命令计算文件md5/sha1/sha256 certutil -hashfile yourfil ...

  7. JavaScript基础视频教程总结(111-120章)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  8. mybatis批量更新报错badsql

    mybatis批量更新时语法写的都对,但是报错,需要在连接上面加上allowMultiQueries=true 示例:jdbc:MySQL://192.168.1.236:3306/test?useU ...

  9. 关于QList<T>的内存释放

    当T为指针类型时,List.clear()不能释放其内存,需加上qDeleteAll()函数, //class Person ---> Person(int id_,QString name_) ...

  10. roadhog如何支持除development和production外的其他环境变量配置

    roadhog的build和start脚本分别对应了env/development和production,但实践中存在第三种开发环境(可能是预发或集成测试),配置和前两种也都不一样,但现在似乎没办法支 ...