canvas画出抖音点赞动画效果demo

canvas画出抖音点赞动画效果demo

粘贴代码可以直接测试(图片自行更换)

html代码

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>点赞demo</title>
        <script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script>
        <script src="../js/praisecanvas.js"></script>
    </head>
    <style type="text/css">
        .canvas-box{
            pointer-events:none;
            z-index:100;
            position:absolute;
            right:20%;
            bottom:50px
        }
        .praise-btn{
            position:absolute;
            right:28%;
            display:inline-block;
            cursor: pointer;
            width:80px;
            height:35px;
        }
        .bdiv {
            position: absolute;
            bottom: 20px;
            height: 20px;
            width: 100%;
            border: 1px solid red;
        }
    </style>
    <body>
        <div class="bdiv">
            <button type="button" id="praisebtn" class="praise-btn" style="" onclick="onPraiseBtnClick()">点赞demo</button>
        </div>
        <div>
            <canvas class="canvas-box" id="praisecanvas" width="160" height="400px" style="" ></canvas>
        </div>
        <script>
             var thumbsUpAni = null;
             var needLoopCount = null;
             function onPraiseBtnClick() {
                if (thumbsUpAni == null)
                    thumbsUpAni = new ThumbsUpAni("praisecanvas");

                needLoopCount += Math.ceil(Math.random() * 5) + 1;

                bullet_renderloop()
             }
             function bullet_renderloop() {
                if (thumbsUpAni != null && needLoopCount > 0) {
                    if (Math.random() * 10 > 8) {
                        thumbsUpAni.start();
                        needLoopCount--;
                        if (needLoopCount <= 0)
                            needLoopCount = 0;
                    }
                }
                requestAnimationFrame(bullet_renderloop);
            }
        </script>
    </body>
</html>
praisecanvas.js 代码

/**
 * >=min && <=max
 * @param min 
 * @param max 
 */
function getRandom(min, max) {
    return min + Math.floor(Math.random() * (max - min + 1))
}
function ThumbsUpAni(canvasId) {
    this.imgsList = [];
    this.context = null;
    this.width = 0;
    this.height = 0;
    this.scanning = false;
    this.renderList=[];
    this.scaleTime = 0.1;// 百分比
    this.loadImages();
    const canvas = document.getElementById(canvasId);
    this.context = canvas.getContext('2d');
    this.width = canvas.width;
    this.height = canvas.height;
} 
ThumbsUpAni.prototype.loadImages = function() {
    const images = [
        '/static/images/praise/bg1.png',
        '/static/images/praise/bg2.png',
        '/static/images/praise/bg3.png',
        '/static/images/praise/bg4.png',
        '/static/images/praise/bg5.png',
        '/static/images/praise/bg6.png',
        '/static/images/praise/gift1_small.png',
        '/static/images/praise/start1_small.png',
        '/static/images/praise/start2_small.png',
        '/static/images/praise/start3_small.png',
    ];
    const promiseAll = [];
    images.forEach((src) => {
        const p = new Promise(function (resolve) {
            const img = new Image;
            img.onerror = img.onload = resolve.bind(null, img);
            img.src = src;
        });
        promiseAll.push(p);
    });
    Promise.all(promiseAll).then((imgsList) => {
        this.imgsList = imgsList.filter((d) => {
            if (d && d.width > 0) return true;
            return false;
        });
        if (this.imgsList.length == 0) {
            dLog('error', 'imgsList load all error');
            return;
        }
    })
}
ThumbsUpAni.prototype.createRender = function() {
    if (this.imgsList.length == 0) return null;
    const basicScale = [0.4, 0.8, 1.0][getRandom(0, 1)];

    const getScale = (diffTime) => {
        if (diffTime < this.scaleTime) {
            return +((diffTime/ this.scaleTime).toFixed(2)) * basicScale;
        } else {
            return basicScale;
        }
    };
    const context = this.context;
    // 随机读取一个图片来渲染
    const image = this.imgsList[getRandom(0, this.imgsList.length - 1)]
    const offset = 20;
    const basicX = this.width / 2 + getRandom(-offset, offset);
    const angle = getRandom(2, 10);
    let ratio = getRandom(10,30)*((getRandom(0, 1) ? 1 : -1));
    const getTranslateX = (diffTime) => {
        if (diffTime < this.scaleTime) {// 放大期间,不进行摇摆位移
            return basicX;
        } else {
            return basicX + ratio*Math.sin(angle*(diffTime - this.scaleTime));
        }
    };

    const getTranslateY = (diffTime) => {
        return image.height / 2 + (this.height - image.height / 2) * (1-diffTime);
    };

    const fadeOutStage = getRandom(14, 18) / 100;
    const getAlpha = (diffTime) => {
        let left = 1 - +diffTime;
        if (left > fadeOutStage) {
            return 1;
        } else {
            return 1 - +((fadeOutStage - left) / fadeOutStage).toFixed(2);
        }
    };

    return (diffTime) => {
        // 差值满了,即结束了 0 ---》 1
        if(diffTime>=1) return true;
        context.save();
        const scale = getScale(diffTime);
        // const rotate = getRotate();
        const translateX = getTranslateX(diffTime);
        const translateY = getTranslateY(diffTime);
        context.translate(translateX, translateY);
        context.scale(scale, scale);
        // context.rotate(rotate * Math.PI / 180);
        context.globalAlpha = getAlpha(diffTime);
        context.drawImage(
            image,
            -image.width / 2,
            -image.height / 2,
            image.width,
            image.height
        );
        context.restore();
    };
}
ThumbsUpAni.prototype.scan = function() {
    this.context.clearRect(0, 0, this.width, this.height);
    this.context.fillStyle = "rgba(255,255,255,0)";
    this.context.fillRect(0,0,200,400);
    let index = 0;
    let length = this.renderList.length;
    if (length > 0) {
        requestFrame(this.scan.bind(this));
        this.scanning = true;
    } else {
        this.scanning = false;
    }
    while (index < length) {
        const child = this.renderList[index];
        if (!child || !child.render || child.render.call(null, (Date.now() - child.timestamp) / child.duration)) {
            // 结束了,删除该动画
            this.renderList.splice(index, 1);
            length--;
        } else {
            // continue
            index++;
        }
    }
}
ThumbsUpAni.prototype.start = function() {
    const render = this.createRender();
    const duration = getRandom(1500, 3000);
    this.renderList.push({
        render,
        duration,
        timestamp: Date.now(),
    });
    if (!this.scanning) {
        this.scanning = true;
        requestFrame(this.scan.bind(this));
    }
    return this;
}
function requestFrame(cb) {
    return (
      window.requestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      function(callback) {
        window.setTimeout(callback, 1000 / 60);
      }
    )(cb);
  }

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值