用canvas实现“鲤鱼跃龙门”~

# 创意投稿大赛 龙年春节

废话不多说,先看效果图

实现代码与思路如下:

第一步 添加canvas画布

html 复制代码
<body style="background-color: rgb(216, 249, 250);margin: 0px;">
    <canvas id="tutorial" width="1520" height="699"></canvas>
    <div class="text">
        <p>鲤鱼跃龙门</p>
        <p>冲呀!</p>
    </div>
</body>

第二步 获取canvas元素

js 复制代码
let canvas = document.getElementById("tutorial");
    //检查支持性
    if (canvas.getContext) {
        var ctx = canvas.getContext("2d"); // 获取渲染上下文
        // ToDo ...
    }

第三步 关于白云部分的绘制和动画

  • 白云的绘制
    用position数组统一设置白云的位置
js 复制代码
    var img = new Image();
    img.src = "./public/cloud.svg";
    var position = [{ x: 20, y: 520 }, { x: 350, y: 560 }, { x: 650, y: 490 }, { x: 930, y: 550 }, { x: 1230, y: 570 }]; //设置白云的位置
    
    img.onload = function () {
            // 开始动画    
        for (let i = 0; i < position.length; i++) {
            ctx.drawImage(img, position[i].x, position[i].y); // 每个图片间隔300像素
       }
    };
  • 白云的动画
    整体的思路就是,结合周期定时器动态设置globalAlpha属性去控制元素的显示与隐藏。
js 复制代码
        // 淡入淡出动画
        function fadeInOut(index) {
            var opacity = 1; // 初始透明度为1
            var increasing = true;
            // 每隔10毫秒执行一次淡入淡出效果
            var intervalId = setInterval(function () {
                var randomNum = Math.random();

                if (increasing) {
                    opacity += randomNum * 0.01; // 透明度逐渐增加
                    if (opacity >= 1) {
                        increasing = false; // 达到最大透明度后开始减少
                    }
                } else {
                    opacity -= randomNum * 0.01; // 透明度逐渐减小
                    if (opacity <= 0) {
                        // increasing = true;
                        clearInterval(intervalId); // 透明度减小到0后清除定时器
                        setTimeout(() => fadeInOut(index), 100); // 等待100ms后重新执行淡入淡出
                    }
                }
                imageOpacities[index] = opacity; // 更新图片透明度
                drawImage(); // 重新绘制

            }, 60);
        }

         // 绘制图片
        function drawImage() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.save(); // 保存当前绘制状态

            // 绘制之前的内容
            ctx.beginPath();
            ctx.moveTo(75, 25);
            //二次贝塞尔曲线
            ctx.quadraticCurveTo(25, 25, 25, 62.5);
            ctx.quadraticCurveTo(25, 100, 50, 100);
            ctx.quadraticCurveTo(50, 120, 30, 125);
            ctx.quadraticCurveTo(60, 120, 65, 100);
            ctx.quadraticCurveTo(125, 100, 125, 62.5);
            ctx.quadraticCurveTo(125, 25, 75, 25);
            ctx.stroke();

            ctx.drawImage(imgDoor, 250, -120);


            // ctx.drawImage(imgFish, 1150, 450, 200, 200);

            for (let i = 0; i < position.length; i++) {
                ctx.globalAlpha = imageOpacities[i];
                ctx.drawImage(img, position[i].x, position[i].y); // 每个图片间隔300像素
            }

            // 绘制鱼
            ctx.globalAlpha = fishOpacity;
            ctx.drawImage(imgFish, fishX, fishY, fishWidth, fishHeight);
            ctx.globalAlpha = 1 - fishOpacity;
            ctx.drawImage(imgDragon, 250, 100, 600, 600);
            ctx.restore(); // 恢复之前保存的绘制状态
        }
        
        
        
        //用处
        img.onload = function () {
            // 开始动画
            position.forEach((item, index) => {
                fadeInOut(index);
            });
        };

第四步 关于鱼的绘制和动画

js 复制代码
        // Fish 图片的初始位置和大小
        var fishX = 1150;
        var fishY = 450;
        var fishWidth = 200;
        var fishHeight = 200;
        // 鱼的透明度
        var fishOpacity = 1;
        
        // 跳跃动画
        function jumpAnimation() {
            var startY = fishY;
            var startx = fishX;
            var maxHeight = startY - 150; // 鱼的最高跳跃高度
            var speed = 2; // 跳跃速度
            var gravity = 0.1; // 重力加速度
            var isRising = true; // 是否处于上升状态

            var jumpInterval = setInterval(function () {
                // 鱼上升
                if (isRising) {
                    fishY -= speed;
                    fishX -= speed + 3;
                    if (fishY <= maxHeight) {
                        isRising = false;
                    }
                }
                // 鱼下落
                else {
                    fishY += speed;
                    fishX -= speed + 3;
                    if (fishY >= startY) {
                        clearInterval(jumpInterval);
                        // 开始消失效果
                        fadeOut();
                    }
                }

                drawImage(); // 重新绘制鱼
            }, 30);
        }

        // 消失效果
        function fadeOut() {
            var opacityDecrement = 0.05; // 透明度减少的步长
            var fadeInterval = setInterval(function () {
                fishOpacity -= opacityDecrement; // 透明度逐渐减小
                if (fishOpacity <= 0) {
                    fishOpacity = 0;
                    clearInterval(fadeInterval);
                }
                drawImage(); // 重新绘制鱼
            }, 50);
        }
 

        //用处
        imgFish.onload = function () {
            // 图片加载完成后开始动画
            setTimeout(function () {
                jumpAnimation(); // 1秒后开始跳跃动画
            }, 1000);
        };

第五步 关于对文字的样式

html 复制代码
<style>
    .text {
        position: absolute;
        top: 28px;
        left: 25px;
    }

    p {
        text-align: center;
        margin: 10px;
    }
</style>

就这,over over ~

祝大家新的一年 诸事顺遂 心想事成!

相关推荐
IT_陈寒4 分钟前
React性能优化:这5个Hook技巧让我的组件渲染效率提升50%(附代码对比)
前端·人工智能·后端
智能化咨询20 分钟前
【Linux】【实战向】Linux 进程替换避坑指南:从理解 bash 阻塞等待,到亲手实现能执行 ls/cd 的 Shell
前端·chrome
Anson Jiang23 分钟前
浏览器标签页管理:使用chrome.tabs API实现新建、切换、抓取内容——Chrome插件开发从入门到精通系列教程06
开发语言·前端·javascript·chrome·ecmascript·chrome devtools·chrome插件
掘金安东尼26 分钟前
黑客劫持:周下载量超20+亿的NPM包被攻击
前端·javascript·面试
剑亦未配妥2 小时前
移动端触摸事件与鼠标事件的触发机制详解
前端·javascript
人工智能训练师7 小时前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
Seveny078 小时前
pnpm相对于npm,yarn的优势
前端·npm·node.js
yddddddy8 小时前
css的基本知识
前端·css
昔人'9 小时前
css `lh`单位
前端·css
前端君9 小时前
实现最大异步并发执行队列
javascript