【canvas】前端创造的图片粒子动画效果:HTML5 Canvas 技术详解

前端创造的图片粒子动画效果:HTML5 Canvas 技术详解

我们将深入探讨如何通过 HTML5 的 Canvas 功能,将上传的图片转换成引人入胜的粒子动画效果。这种效果将图片分解成小粒子,并在用户与它们交互时产生动态变化。我们将分步骤详细解析代码,让你能够理解每一行代码的作用,并自己实现这一效果。
在这里插入图片描述

环境准备

首先,你需要一个简单的 HTML 元素和一些样式设置:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Particle Image Animation from Uploaded Image</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
            overflow: hidden;
        }
        canvas, input {
            display: block;
            margin: auto;
        }
    </style>
</head>
<body>
    <input type="file" id="upload" accept="image/*">
    <canvas id="canvas" hidden></canvas>
</body>
</html>

这段 HTML 设置了一个文件输入控件供用户上传图片,以及一个 Canvas 元素用于渲染动画效果。样式使页面内容居中显示,并将背景设置为浅灰色。

JavaScript 部分

JavaScript 脚本是这个效果的核心,下面我们逐一解析每个部分的功能。

1. 初始化和载入图片:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let particles = [];
const numOfParticles = 5000;
const uploadInput = document.getElementById('upload');

uploadInput.addEventListener('change', function(event) {
    const file = event.target.files[0];
    if (file && file.type.startsWith('image')) {
        const reader = new FileReader();
        reader.onload = function(e) {
            const maxSize = 500; // 最大尺寸
                        let width = img.width;
                        let height = img.height;
                        let scale = Math.min(maxSize / width, maxSize / height);
                        if (scale < 1) {
                            width *= scale;
                            height *= scale;
                        }
                        canvas.width = width;
                        canvas.height = height;
                        ctx.drawImage(img, 0, 0, width, height);
                        canvas.hidden = false;
                        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                        createParticles(imageData);
                        animate();
            };
        };
        reader.readAsDataURL(file);
    }
});

在这部分代码中,我们首先获取 Canvas 元素并配置基本画布(context)。监听文件输入控件的变化事件,当用户选择一个图片文件时,使用 FileReader 对象读取文件内容,将其转换为 Base64 编码的 URL,然后载入 <img> 元素。图片载入完毕后,把它绘制到 Canvas 上,然后提取图片的像素数据。

2. 创建粒子:
function createParticles(imageData) {
    particles = [];
    const { width, height } = imageData;
    for (let i = 0; i < numOfParticles; i++) {
        const x = Math.random() * width;
        const y = Math.random() * height;
        const color = imageData.data[(~~y * width + ~~x) * 4];
        particles.push(new Particle(x, y, color));
    }
}

这个函数根据图片的像素数据随机生成指定数量的粒子。每个粒子具有位置(x,y)和基于图片某一点的颜色。粒子的初始位置是随机分布的。

3. 定义粒子对象:
function Particle(x, y, color) {
    this.x = x;
    this.originalX = x;
    this.y = y;
    this.originalY = y;
    this.color = `rgba(${color},${color},${color}, 0.5)`;

    this.draw = function() {
        ctx.fillStyle = this.color;
        ctx.fillRect(this.x, this.y, 2, 2);
    };

    this.update = function() {
        let dx = this.originalX - this.x;
        let dy = this.originalY - this.y;
        this.x += dx * 0.1;
        this.y += dy * 0.1;

        this.draw();
    };
}

粒子对象具有 drawupdate 方法。draw 方法用来在 Canvas 上绘制粒子,update 方法则负责更新粒子的位置,使它们逐渐回到原始位置。

4. 动画循环和鼠标交互:
function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    particles.forEach(particle => particle.update());
    requestAnimationFrame(animate);
}

animate 函数清空画布并更新所有粒子的位置,然后通过 requestAnimationFrame 递归调用自身以形成动画循环。

完整代码

复制这段代码到一个.html文件,可以直接在浏览器允许该demo,实际操作一番。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Particle Image Animation from Uploaded Image</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
            overflow: hidden;
        }

        canvas,
        input {
            display: block;
            margin: auto;
        }
    </style>
</head>

<body>
    <input type="file" id="upload" accept="image/*">
    <canvas id="canvas" hidden></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        let particles = [];
        const numOfParticles = 5000;
        const uploadInput = document.getElementById('upload');

        uploadInput.addEventListener('change', function (event) {
            const file = event.target.files[0];
            if (file && file.type.startsWith('image')) {
                const reader = new FileReader();
                reader.onload = function (e) {
                    const img = new Image();
                    img.src = e.target.result;
                    img.onload = function () {
                        const maxSize = 500; // 最大尺寸
                        let width = img.width;
                        let height = img.height;
                        let scale = Math.min(maxSize / width, maxSize / height);
                        if (scale < 1) {
                            width *= scale;
                            height *= scale;
                        }
                        canvas.width = width;
                        canvas.height = height;
                        ctx.drawImage(img, 0, 0, width, height);
                        canvas.hidden = false;
                        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                        createParticles(imageData);
                        animate();
                    };
                };
                reader.readAsDataURL(file);
            }
        });

        function createParticles(imageData) {
            particles = [];
            const { width, height } = imageData;
            for (let i = 0; i < numOfParticles; i++) {
                const x = Math.random() * width;
                const y = Math.random() * height;
                const color = imageData.data[(~~y * width + ~~x) * 4];
                particles.push(new Particle(x, y, color));
            }
        }

        function Particle(x, y, color) {
            this.x = x;
            this.originalX = x;
            this.y = y;
            this.originalY = y;
            this.color = `rgba(${color},${color},${color}, 0.5)`;

            this.draw = function () {
                ctx.fillStyle = this.color;
                ctx.fillRect(this.x, this.y, 2, 2);
            };

            this.update = function () {
                let dx = this.originalX - this.x;
                let dy = this.originalY - this.y;
                this.x += dx * 0.1;
                this.y += dy * 0.1;

                this.draw();
            };
        }

        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            particles.forEach(particle => particle.update());
            requestAnimationFrame(animate);
        }

        canvas.addEventListener('mousemove', function (e) {
            const rect = canvas.getBoundingClientRect();
            const mouseX = e.clientX - rect.left;
            const mouseY = e.clientY - rect.top;

            particles.forEach(particle => {
                const dx = mouseX - particle.x;
                const dy = mouseY - particle.y;
                const dist = Math.sqrt(dx * dx + dy * dy);

                if (dist < 50) {
                    const angle = Math.atan2(dy, dx);
                    particle.x -= Math.cos(angle);
                    particle.y -= Math.sin(angle);
                }
            });
        });
    </script>
</body>

</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/574902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW专栏九、类的应用

一、类的应用 接上一章"类" 类在项目中&#xff0c;一般会在类的私有成员簇内&#xff0c;包含一个数据类型为参数类的队列。 例如网口类&#xff0c;里面实际会包含很多信息&#xff0c;有IP地址和端口等等参数。这些参数如果不放在队列引用中缓存下来&#xff0c;…

DevOps(十四)怎么实现Gitlab更新后Jenkins自动发布

目录 1、在 Jenkins 中安装 GitLab 插件 2、在 GitLab 中创建一个访问令牌(Access Token) 3、在 Jenkins 中配置 GitLab 连接 4、在 Jenkins 中创建一个新的任务(Job) 5、在 GitLab 中配置 Webhook 6、以下是一些补充说明和建议 持续集成的一个特点就是开发可以随时提交&…

微服务组件-反向代理(Nginx)

微服务组件-反向代理(Nginx) Nginx 基本概念 1、nginx是什么&#xff1f; ①、Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器同时也提供了IMAP/POP3/SMTP服务。它是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&a…

TiDB 6.x 新特性解读 | Collation 规则

对数据库而言&#xff0c;合适的字符集和 collation 规则能够大大提升使用者运维和分析的效率。TiDB 从 v4.0 开始支持新 collation 规则&#xff0c;并于 TiDB 6.0 版本进行了更新。本文将深入解读 Collation 规则在 TiDB 6.0 中的变更和应用。 引 这里的“引”&#xff0c;…

Oracle 监控 SQL 精选 (一)

Oracle数据库的监控通常涉及性能、空间、会话、对象、备份、安全等多个层面。 有效的监控可以帮助 DBA 及时发现和解决问题&#xff0c;提高数据库的稳定性和性能&#xff0c;保障企业的数据安全和业务连续性。 常用的监控指标有&#xff1a; 性能指标&#xff1a; 查询响应时间…

产品推荐 | BittWare基于Altera Agilex“M FPGA的lA-860m加速卡

01 产品概述 BittWare的lA-860m是一款Altera Agilex“M系列FPGA卡&#xff0c;针对吞吐量和内存密集型应用进行了优化。M 系列 FPGA 具有广泛的内存层次结构&#xff0c;包括集成高带宽存储器 &#xff08;HBM2e&#xff09; 和硬内存片上网络 &#xff08;NoC&#xff09;&am…

【QT】ROS2 Humble联合使用QT教程

【QT】ROS2 Humble联合使用QT教程 文章目录 【QT】ROS2 Humble联合使用QT教程1. 安装ROSProjectManager插件2. 创建ROS项目3.一个快速体验的demoReference 环境的具体信息如下&#xff1a; ubunt 22.04ros2 humbleQt Creator 13.0.0ROS ProjectManager 13.0.0 本文建立在已经…

Vivado-IP-DDS and Testbench Learning

DDS内部结构 实现流程 首先新建一个工程&#xff0c;创建bd文件&#xff0c;添加DDS Compiler核&#xff0c;此处不多赘述 Block Design 在观测输出的信号时&#xff0c;需要将最高位符号位的信号取反&#xff0c;这样才能输出正弦波&#xff0c;否则输出的波形如下图所示 将t…

OpenStack云计算(十)——OpenStack虚拟机实例管理,增加一个计算节点并进行实例冷迁移,增加一个计算节点的步骤,实例冷迁移的操作方法

项目实训一 本实训任务对实验环境要求较高&#xff0c;而且过程比较复杂&#xff0c;涉及的步骤非常多&#xff0c;有一定难度&#xff0c;可根据需要选做。可以考虑改为直接观看相关的微课视频 【实训题目】 增加一个计算节点并进行实例冷迁移 【实训目的】 熟悉增加一个…

实验 1--SQL Server2008数据库开发环境

文章目录 实验 1--SQL Server2008数据库开发环境2.4.1 实验目的2.4.2 实验准备2.4.3 实验内容1.利用 SSMS 访问系统自带的Report Server 数据库。2.熟悉了解 SMSS对象资源管理器树形菜单相关选择项的功能。(1)右键单击数据库Report Server&#xff0c;查看并使用相关功能;(2)选…

K8s: 部署 kubernetes dashboard

部署 Dashboard K8s 官方有一个项目叫 dashboard&#xff0c;通过这个项目更方便监控集群的状态 官方地址: https://github.com/kubernetes/dashboard 通常我们通过命令行 $ kubectl get po -n kube-system 能够查看到集群所有的组件&#xff0c;但这样的方式比较不太直观 …

算法学习002-填数游戏 中小学算法思维学习 信奥算法解析 c++实现

目录 C填数游戏 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C填数游戏 一、题目要求 1、编程实现 在小学奥数中经常会看到一些填数字的游戏&#xff0c;如下图所示&#xff0c;其中每个…

【Web】第三次

【Web】第三次 1.完成学校官方网站页面制作2.使用动画完成过渡变换效果 1.完成学校官方网站页面制作 2.使用动画完成过渡变换效果 1.完成学校官方网站页面制作 html&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://…

OpenCV 实现重新映射

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV 实现霍夫圆变换 下一篇 :OpenCV实现仿射变换 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 一个。使用 OpenCV 函数 cv&#xff1a;&#xff1a;remap 实现简单的重新…

Socket编程实验

文章目录 服务端&#xff1a;客户端&#xff1a;使用说明&#xff1a;封装后服务端&#xff1a;封装后客户端 听学弟学妹们反馈&#xff0c;好像老师发的socket编程实验指导里的代码跑不起来。 今天花了一大把时间写了下socket编程代码 现在附上能跑的c代码&#xff1a; 最重要…

nosql数据库 redis

一、介绍 1、redis与mysql的区别&#xff1a; Redis是一种基于键值对的内存数据库&#xff0c;数据存储在内存中&#xff0c;因此读写速度非常快。它支持多种数据结构&#xff0c;如字符串、哈希、列表等。 MySQL是一种关系型数据库&#xff0c;数据以表格的形式组织存储在磁…

12.6.1 实验5:IOS恢复

1、实验目的 通过本实验可以掌握&#xff1a; copy方式恢复IOS的步骤。TFTPDNLD方式恢复IOS的步骤。Xmodem方式恢复IOS的步骤。 2、实验拓扑 路由器IOS恢复的实验拓扑如下图所示。 3、实验步骤 如果工作中不慎误删除路由器IOS&#xff0c;或者升级了错误版本的IOS&#xff…

Sylar C++高性能服务器学习记录06 【线程模块-代码分析篇】

早在19年5月就在某站上看到sylar的视频了&#xff0c;一直认为这是一个非常不错的视频&#xff0c;还有幸加了sylar本人的wx&#xff0c;由于本人一直是自学编程&#xff0c;基础不扎实&#xff0c;也没有任何人的督促&#xff0c;没能坚持下去&#xff0c;每每想起倍感惋惜。恰…

机器学习day3

一、距离度量 1.欧氏距离 2.曼哈顿距离 3.切比雪夫距离 4.闵可夫斯基距离 二、特征与处理 1.数据归一化 数据归一化是一种将数据按比例缩放&#xff0c;使之落入一个小的特定区间的过程。 代码实战 运行结果 2.数据标准化 数据标准化是将数据按照其均值和标准差进行缩放的过…

2024最新版JavaScript逆向爬虫教程-------基础篇之面向对象

目录 一、概念二、对象的创建和操作2.1 JavaScript创建对象的方式2.2 对象属性操作的控制2.3 理解JavaScript创建对象2.3.1 工厂模式2.3.2 构造函数2.3.3 原型构造函数 三、继承3.1 通过原型链实现继承3.2 借用构造函数实现继承3.3 寄生组合式继承3.3.1 对象的原型式继承3.3.2 …