用canvas实现红心飘飘的动画效果
两周前,项目里需要实现一个红心飘飘的点赞效果。抓耳挠腮了老半天,看了几篇大佬的文章,终于算是摸了个七七八八。不禁长叹一声,还是菜啊。先来看一下效果:(传送门进去点一波)
一、Bezier曲线运动轨迹
其实用大白话描述一下需求就是让一个红心图片沿着贝塞尔曲线的轨迹走,然后边走边消失。核心在于得到贝塞尔曲线上的一系列点。本文不会讲解贝塞尔曲线的原理,因为大佬们已经讲过了,而且讲的比我好。参考文章如下:
其中第二篇文章讲到了生成二阶和三阶贝塞尔曲线可以使用canvas自带的方法:quadraticCurveTo
和bezierCurveTo
,而高阶的则先得到曲线上一系列的点,然后顺次连接这些点来拟合高阶的贝塞尔曲线。没错,我们要的就是这一系列的点,有了这些点,就可以控制红心的轨迹了。下面是我基于作者的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实现红心飘飘的动画效果的更多相关文章
- 多种方法实现Loading(加载)动画效果
当我们ajax提交一个按钮的时候,给那个按钮来个Loading效果会高端很多,体验也会上升个层次. 既能让用户知道正在提交中,也能防止二次提交,好处多多呢.
- (转)多种方法实现Loading(加载)动画效果
当我们ajax提交一个按钮的时候,给那个按钮来个Loading效果会高端很多,体验也会上升个层次. 既能让用户知道正在提交中,也能防止二次提交,好处多多呢.
- html5 canvas首屏自适应背景动画循环效果代码
模板描述:html5 canvas首屏自适应背景动画循环效果代码 由于动态图太大,怕以后服务器受不了,所以现在都改为静态图了,大家点击演示地址一样的,希望大家喜欢,你们的支持就是小海的动力!! 欢迎大 ...
- 基于canvas实现物理运动效果与动画效果(一)
一.为什么要写这篇文章 某年某月某时某种原因,我在慕课网上看到了一个大神实现了关于小球的抛物线运动的代码,心中很是欣喜,故而写这篇文章来向这位大神致敬,同时也为了弥补自己在运动效果和动画效果制作方面的 ...
- 7个惊艳的HTML5 Canvas动画效果及源码
HTML5非常强大,尤其是现在大部分浏览器都支持HTML5和CSS3,用HTML5制作的动画也多了起来.另外,Canvas上绘制图形非常简单,本文就分享了一些强大的HTML5 Cnavas动画,一起来 ...
- HTML5 Canvas动画效果演示
HTML5 Canvas动画效果演示 主要思想: 首先要准备一张有连续帧的图片,然后利用HTML5 Canvas的draw方法在不同的时间 间隔绘制不同的帧,这样看起来就像动画在播放. 关键技术点: ...
- 使用Canvas实现动画效果 | DKlogs -- 设计 | 生活
使用Canvas实现动画效果 | DKlogs -- 设计 | 生活 使用Canvas实现动画效果
- HTML5 Canvas动画效果演示 - 流浪的鱼 - 博客频道 - CSDN.NET
HTML5 Canvas动画效果演示 - 流浪的鱼 - 博客频道 - CSDN.NET HTML5 Canvas动画效果演示
- HTML5 Canvas动画效果实现原理
在线演示 使用HTML5画布可以帮助我们高速实现简单的动画效果.基本原理例如以下: 每隔一定时间绘制图形而且清除图形,用来模拟出一个动画过程,能够使用context.clearRect(0, 0, x ...
随机推荐
- 【Mybatis】MyBatis之Sql配置文件的使用(四)
上一章[Mybatis]MyBatis对表执行CRUD操作(三),已经讲了基本操作,本章介绍Sql配置文件中常用功能 1.插入返回主键 2.参数值的获取方式 3.resultMap使用 插入返回主键 ...
- session实现原理(阿里面试题)
问: 当用户登录某网站后,向服务器发送一个请求,服务器如何判断是这个用户请求的 首先,你要明白一点,最初http协议在设计的时候,主要面向当时的web1.0网站,他们不需要知道是谁来访问,只需要向外界 ...
- docker服务各个模块
docker容器官网:https://hub.docker.com/ 一.centos7.4中指定安装docker版本 1)默认yum源安装的docker版本为docker1.3.性能偏低,不支持k8 ...
- Python从入门到精通之Second!
初识Python基础! -基础 -扩展名可以是任意的,但是导入模块时,如果不是.py文件就会报错. -两种执行方式 1.python解释器 文件名.py 文件路径 -Li ...
- SpringBoot_定制banner
SpringBoot项目在启动时会打印一个banner 这个banner 是可以定制的, 在resources 目录下创建一个banner.txt 文件,在这个文件中写入的文本将在项目启动时打印出来. ...
- PHP单一文件入口框架简析
<?php /** * PHP单一文件框架设计简析 * 1.MVC架构实现 * 2.URL路由原理 */ //URL路由原理 /** * 路由作用 * 获取url中的c和a变量,执行c类对应的方 ...
- HDU 2147 P/N博弈
点这里去做题 如图 找必胜点和必败点, 1.终点为必胜点 2.所有能一步走到必胜点的都是必败点 3.每一步都只能走到必败点的是必胜点 #include<bits/stdc++.h> usi ...
- Ubuntu 16——安装——ns2.35和nam
Ubuntu 16.04 安装ns2.35+nam 总结出以下安装步骤 1: 更新源 sudo apt-get update #更新源列表 sudo apt-get upgrade #更新已经安装的包 ...
- repo跟svn的区别
Git与SVN区别 Git和SVN正好相反,git提倡开发时拉分支,各干各的,相互独立,发版本时打标签:而svn呢,平时大家都在主干上干活,发版本时拉个分支,所以呢,svn经常会提交冲突,经常要合并代 ...
- JavaScript自定义鼠标右键菜单
下面为JavaScript代码 window.onload = function () { //好友列表 var f = 0; //判断指定id的元素在页面中是否存在 if (document.get ...