最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Canvas如何绘制爱心动画

    正文概述 掘金(jsmask)   2021-08-16   683

    这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

    前言

    世上有两样东西不可直视,一是太阳,二是人心。 ——东野圭吾《白夜行》

    介绍

    七夕将至,今天就介绍给大家如何绘制一个心形动画!!保证你的女神看完神魂颠倒!!!

    Canvas如何绘制爱心动画

    如何绘制心型

    1.笛卡尔心形

    这里先给大家讲一个故事,很久很久以前,数学家笛卡儿曾流落到瑞典,邂逅了美丽的瑞典公主克里斯蒂娜。笛卡儿发现克里斯蒂娜公主聪明伶俐,便做起了公主的数学老师,于是二人完全沉浸在了数学的世界中。国王知道后,认为笛卡儿配不上自己的女儿,不但强行拆散了他们,还没收了之后笛卡儿写给公主的所有信件。后来,笛卡儿染上黑死病,在临死前给公主寄去了最后一封信,信中只有一行字:舔狗舔狗一无所有!哦哦,不是这句,其实信中只有一句公式:r=a(1-sinθ)。最后,这句公式落到公主手上,公主在纸上建立了极坐标系,用笔在上面描下方程的点,终于解开了这行字的秘密——这就是美丽的心形线。

    const canvas = document.createElement("canvas");
    let w = canvas.width = 800;
    let h = canvas.height = 600;
    let ctx = canvas.getContext("2d");
    let a = 72;
    ctx.save();
    ctx.fillStyle = "#f23232";
    ctx.translate(w/2, h/2);
    ctx.scale(1, -1);
    ctx.beginPath();
    ctx.moveTo(0, 0);
    let r = 0, x = 0, y = 0;
    for (let i = -5; i < 360; i++) {
        r = a * (1 - Math.sin(i * Math.PI / 180));
        x = r * Math.cos(i * Math.PI / 180);
        y = r * Math.sin(i * Math.PI / 180);
        ctx.lineTo(x, y);
    }
    ctx.fill();
    ctx.closePath();
    ctx.restore();
    document.body.appendChild(canvas);
    

    Canvas如何绘制爱心动画

    2.双椭圆心形

    虽然笛卡尔的故事很浪漫,它也是标准的心形曲线,但是确实不符合审美标准。所以有了另外一种心形曲线。

    const canvas = document.createElement("canvas");
    let w = canvas.width = 800;
    let h = canvas.height = 600;
    let ctx = canvas.getContext("2d");
    ctx.save();
    ctx.beginPath();
    ctx.fillStyle = "#f23232";
    ctx.translate(w/2, h/2);
    ctx.rotate(Math.PI)
    let r = 0, a = 20, start = 0, end = 0;
    for (let i = 0; i < 1000; i++) {
        start += Math.PI * 2 / 1000;
        end = start + Math.PI * 2 / 1000;
        r = a * Math.sqrt(225 / (17 - 16 * Math.sin(start) * Math.sqrt(Math.cos(start) ** 2)));
        ctx.arc(0, 0, r, start, end, false);
    }
    ctx.fill();
    ctx.closePath();
    ctx.restore();
    document.body.appendChild(canvas);
    

    Canvas如何绘制爱心动画

    3.完美心形

    双椭圆心形是在转折的地方还不够平滑,依然缺乏部分美感,出现一直完美的心形曲线。

    const canvas = document.createElement("canvas");
    let w = canvas.width = 800;
    let h = canvas.height = 600;
    let ctx = canvas.getContext("2d");
    ctx.save();
    ctx.beginPath();
    ctx.fillStyle = "#f23232";
    ctx.translate(w/2, h/2);
    ctx.scale(1,-1);
    ctx.moveTo(0,0);
    let angle=0,x=0,y=0,a=6;
    for (let i = 0; i < 30; i+=0.2) {
        angle = i / Math.PI;
        x = a * (16*Math.sin(angle)**3);
        y = a * (13*Math.cos(angle)-5*Math.cos(2*angle)-2*Math.cos(3*angle)-Math.cos(4*angle));
        ctx.lineTo(x,y);
    }
    ctx.fill();
    ctx.closePath();
    ctx.restore();
    document.body.appendChild(canvas);
    

    Canvas如何绘制爱心动画

    心形动画

    1.基础结构

    <style>
        *{
            padding: 0;
            margin: 0;
        }
        html,body{
            width: 100%;
            height: 100vh;
            overflow: hidden;
        }
        #canvas{
            width: 100%;
            height: 100%;
        }
    </style>
    <canvas id="canvas"></canvas>
    <script type="module" src="./app.js"></script>
    
    /*app.js*/
    import Heart from "./js/heart.js"
    window.onLoad = new Heart().init();
    
    /*heart.js*/
    class Heart {
      constructor(optoins) {}
      init() {}
      render() {}
      reset() {}
    }
    export default Heart;
    

    这里我们把基础结构先写出来,利用module模式来导入模块,接下来来我们集中精力写heart.js就行了。

    2.定义传参

    我们首先要分析都需要什么参数,什么参数是可以从外界传递过来的,什么是自己本身的。

    我们着手写constructor,不多说先看代码:

    constructor(optoins) {
        this.backgroundColor = "black";   // 背景色
        this.strokeColor = "white";       // 前景色
        this.lineWidth = 2;               // 线宽
        this.step = 20;                   // 线段间隔
        Object.assign(this, optoins);
        this.canvas = null;               // 画布
        this.ctx = null;                  // 环境
        this.w = 0;                       // 画布宽度
        this.h = 0;                       // 画布高度
        this.offset = 0;                  // 频率修正值
        this.imageBuffer = [];            // 图像buffer信息
      }
    

    首先,我们定义背景色backgroundColor,前景色strokeColor,线宽lineWidth,线段间隔step这四个属性可以通过外界传入进行浅拷贝从而改变其参数值。经过分析我们要画心形肯定要有写出一套心形数据存储起来,这里imageBuffer就是来收集生成的心形数据的容器。

    3.初始化

    init() {
        this.canvas = document.getElementById("canvas");
        this.ctx = this.canvas.getContext("2d");
        window.addEventListener("resize", this.reset.bind(this));
        this.reset();
        this.render();
    }
    
    reset() {
        const {strokeColor, lineWidth} = this;
        this.w = this.canvas.width = window.innerWidth;
        this.h = this.canvas.height = window.innerHeight;
        this.ctx.strokeStyle = strokeColor;
        this.ctx.lineWidth = lineWidth;
        this._storeHeartInBuffer();
    }
    

    这里,初始化先获取到画布容器定义环境,监听屏幕改变事件。宽高等一些参数可能随着屏幕变化而变化,我就直接放到reset方法里面,初始化直接调用他首次获取到。另外,我也要获取屏幕改变后的imageBuffer信息,同样,我们要再获取一遍。

    4.绘制心形

    我这里要绘制的肯定是一个符合美感的完美心形,就利用刚刚的心形曲线公式来绘制。

    _storeHeartInBuffer() {
        const {ctx, w, h} = this;
        ctx.beginPath();
        for (let angle = 0; angle < Math.PI * 2; angle += 0.01) {
            let r = Math.min(w, h) * 0.025;
            let x = r * 16 * Math.pow(Math.sin(angle), 3);
            let y = -r * (13 * Math.cos(angle) - 5 * Math.cos(2 * angle) - 2 * Math.cos(3 * angle) - Math.cos(4 * angle));
            ctx.lineTo(w / 2 + x, h * 0.45 + y);
        }
        ctx.fill();
    ​
        let image = ctx.getImageData(0, 0, w, h);
        this.imageBuffer = new Uint32Array(image.data.buffer);
    }
    

    不过,这次我们绘制不是让他展示出来而是通过getImageData拿到他信息从而转化成数组方便提取信。

    5.渲染背景与线

    render(delta) {
        const {w, h, render} = this;
        requestAnimationFrame(render.bind(this));
        this.ctx.fillRect(0, 0, w, h);
        this.offset = delta / 25;
        this._drawBackground();
        this._drawHeartLines();
    }
    

    requestAnimationFrame去做实时的渲染更新。这里定义好执行绘制背景与线段的方法。接下来,就终于到了主绘制逻辑了!

    _drawBackground() {
        const {ctx, w, h, backgroundColor} = this;
        ctx.save();
        ctx.fillStyle = backgroundColor;
        ctx.fillRect(0, 0, w, h);
        ctx.restore();
    }
    
    _drawHeartLines() {
        const {h, step} = this;
        for (let y = 0; y < h; y += step) {
            this._drawHeartLine(y + this.offset % step);
        }
    }
    
    _drawHeartLine(y) {
        const {ctx, w, imageBuffer, step} = this;
        ctx.beginPath();
        let _y;
        for (let x = 0; x < w; x += 1) {
            if (imageBuffer[Math.floor(y) * w + x]) {
                _y = y + step / 2;
            } else {
                _y = y;
            }
            ctx.lineTo(x, _y);
        }
        ctx.stroke();
    }
    

    这里绘制背景很简单不做赘述。绘制线段每隔step绘制一条。绘制的时候就需要用到我们的主角了imageBuffer里面的心形信息了。如果判断有当前点对应了心形所在的点,那么给他的y轴加一个偏移量再进行绘制。

    这里我就完成了,是不是很容易,演示地址

    拓展与延伸

    当我们学会绘制一个心形的同时其实可以有更多的动画形式去展现,比如很多粒子效果换成不同颜色的爱心,再或者心中间镂空,其的边线换成粒子效果。所以只有你想不到没有你做不到。


    七夕要来了,祝天下有情人终成眷属!不多说了,身为单身汪的我先躲起来了~~~


    起源地下载网 » Canvas如何绘制爱心动画

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元