用canvas实现红心飘飘的动画效果

时间:2022-12-11 23:58:36

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

用canvas实现红心飘飘的动画效果

一、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值,就能计算出曲线上的某一点的坐标。

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

用canvas实现红心飘飘的动画效果

当将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. python 字符串技巧 from python cookbook

    所有数据进入程序中都只是一串字节 英文字符占一个字节 汉语是两个字节 一字节byte=8bit Unicode字符串为每种语言的每种字符设定了统一并且唯一的二进制编码 big = r'This is ...

  2. java重载和重写的区别

    一.重载(Overloading) (1) 方法重载是让类以统一的方式处理不同类型数据的一种手段.多个同名函数同时存在,具有不同的参数个数/类型. 重载Overloading是一个类中多态性的一种表现 ...

  3. SQLServer安装错误之--->无法打开项 UNKNOWN\Components\DA42BC89BF25F5BD0AF18C3B9B1A1EE8\c1c4f01781cc94c4c8fb1542c0981a2a

    – 错误 1402.无法打开项 UNKNOWN\Components\7ABFE44842C12B390AF18C3B9B1A1EE8\54D21D49F3A8C1C49AC11A1B6445A83E ...

  4. IDEA中PlantUML的使用

    PlantUML官网地址:http://www.plantuml.com/ IDEA中安装PlantUML Plugins File-->Settings-->Plugins-->I ...

  5. $modal

    $scope.open = function (size,data) {var modalInstance = $modal.open({ templateUrl: 'myModalContent.h ...

  6. spring IOC源码分析(3)

    1.IOC容器的依赖注入 Spring中,依赖注入是在用户第一次向IOC容器索要Bean时触发的(通过getBean方法). 在BeanFactory中我们看到getBean(String…)函数,它 ...

  7. 利用Ajax把前端的数据封装成JSON格式发送到服务器端并写成XML格式在服务器的硬盘上

    1.首先要在前端把要发送的东西(这里是一个实例化的car对象)都准备好,利用Ajax发送到服务器端,代码如下: <html xmlns="http://www.w3.org/1999/ ...

  8. BestCoder Round 59 &lpar;HDOJ 5500&rpar; Reorder the Books

    Problem Description dxy has a collection of a series of books called “The Stories of SDOI”,There are ...

  9. visual studio属性管理器

    位于 视图->其它窗口 项目配置属性,这个项目的属性别的项目也需要的时候可以把属性页复制到新项目. 以及配置单个项目的不同构建方案

  10. avalon2学习教程08插入移除操作

    本节介绍的ms-if指令与ms-visible很相似,都是让某元素"看不见",不同的是ms-visible是通过CSS实现,ms-if是通过移除插入节点实现. ms-if的用法与1 ...