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

news2024/11/19 8:32:20

前端创造的图片粒子动画效果: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>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue快速入门(四十三)axios模块的安装与引入

步骤很详细&#xff0c;直接上教程 上一篇 在项目目录打开终端 输入以下命令安装axios npm i axios重新打开项目即可完成按照 测试 源码 main.js import Vue from vue import App from ./App.vue//全局引入axios // 引入axios import axios from axios; // 挂载到vue原型…

HarmonyOS har制作与引用

1. HarmonyOS har制作与引用 1.1 介绍 HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP&#xff0c;不能独立安装运行在设备上&#…

Linux 进程概念和状态

目录 一、冯诺依曼体系结构 二、操作系统 1.概念 2.理解操作系统的管理 硬件和管理 为什么要有操作系统 三、进程的概念 PCB&#xff1a; 进程的删除和子进程的创建 删除 创建子进程 四、进程的状态 七种状态&#xff1a; 实验查看部分状态&#xff1a; R&#x…

2.7设计模式——Proxy 代理模式(结构型)

意图 为其它对象提供一种代理以控制这个对象的访问。 结构 Proxy保存一个引用使得代理可以访问实体&#xff1b;提供一个与Subject的接口相同的接口&#xff0c;使代理可以用来替代实体&#xff1b;控制实体的存取&#xff0c;并可能负责创建和删除它&#xff1b;其他功能依赖…

【【gitlab解决git Clone 出现 Permission denied, please try again.】】

【gitlab解决git Clone 出现 Permission denied, please try again.】 问题解决随便找一个地方 点击右键输入ssh -keygen -C "邮件"显示结果输入 登录gitlab然后再次git Clone就可以了。 问题 git clone的时候出现 Permission denied, please try again 解决 随便…

ChatGPT付费创作系统V2.8.4独立版 WEB+H5+小程序端 (新增Pika视频+短信宝+DALL-E-3+Midjourney接口)

小狐狸GPT付费体验系统最新版系统是一款基于ThinkPHP框架开发的AI问答小程序&#xff0c;是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。当前全民热议ChatGPT&#xff0c;流量超级大&#xff0c;引流不要太简单&#xff01;一键下单即可拥有自己的GPT&#xff01;无限多…

【draw.io的使用心得介绍】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

K8S--常用的命令

原文网址&#xff1a;K8S--常用的命令-CSDN博客 简介 本文介绍K8S常用的命令。 官网 简略用法&#xff1a;https://kubernetes.io/zh-cn/docs/reference/kubectl/ 详细用法&#xff08;有示例&#xff09;&#xff1a;https://kubernetes.io/docs/reference/generated/kub…

JS设计模式-透过现象看本质

JS设计模式-透过现象看本质 设计模式SOLID设计原则创建型构造器模式工厂模式 - 简单工厂工厂模式 - 抽象工厂&#xff08;开发封闭原则&#xff09;构造器和简单、抽象工厂的区别 单例模式原型模式 结构型装饰器模式适配器模式代理模式事件代理 - 事件冒泡虚拟代理 - 通过Image…

前端开发攻略---用原生JS在网页中也能实现语音识别

1、语音识别的过程 语音识别涉及三个过程&#xff1a;首先&#xff0c;需要设备的麦克风接收这段语音&#xff1b;其次&#xff0c;语音识别服务器会根据一系列语法 (基本上&#xff0c;语法是你希望在具体的应用中能够识别出来的词汇) 来检查这段语音&#xff1b;最后&#xf…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Text Edit的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Text Edit的使用及说明 文章编号&#xff…

Jsoncpp搭建交叉编译环境(移植到arm)

1. 官网下载源码 github地址&#xff1a;GitHub - open-source-parsers/jsoncpp at update 2. 交叉编译环境 当前平台/开发平台-编译环境&#xff1a; [rootlocalroot ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalroot ~]# uname -a Lin…

大型语言模型LLM的数据管理与应用

大型语言模型&#xff08;LLM&#xff09;风靡全球&#xff0c;尤其是 OpenAI 的最新发展。LLMs 的魅力来自于其理解、解释和生成人类语言的能力&#xff0c;而这曾被认为是人类的专属领域。像 CoPilot 这样的工具正在迅速融入开发人员的日常生活&#xff0c;而以 ChatGPT 为动…

使用 Redux 管理全局状态

Redux 是个状态集中管理框架&#xff0c;状态可以跨组件共享&#xff0c;状态更新后&#xff0c;调用监听器。其实状态可以认为就是个全局对象&#xff0c;为什么要做一个框架来管理呢&#xff1f;如果我们自己使用一个全解字典来管理状态是不是也行&#xff1f;如果不做任何控…

不同语言在算法使用方面的差异(Java 、C++篇)

由于我认为的会了是能得到结果了&#xff0c;所以我亲自去把题解的C代码给改成了Java的&#xff0c;尽管代码和逻辑上的高度统一。编译器还是报错了。 第三个死都过不去。而且后面的还超时了。 这使我十分怀疑是不是超时或者空间不够所导致的。但是去问讯飞星火&#xff0c;它…

自有道 更从容——林肯携“四大美式客厅”登陆北京国际车展,以传世豪华 优雅从容为品牌注入全新内涵

【北京 2024年4月25日】传世豪华&#xff0c;优雅从容。今日&#xff0c;第十八届北京国际汽车展览会正式启幕。作为美式豪华品牌的引领者&#xff0c;林肯在此次北京车展为“豪华 自有其道”注入全新内涵&#xff0c;并以车展首创的四大美式客厅形式诠释不同的人生境界&#x…

AWS制作WordPress在国内外的利弊?

AWS作为全球领先的云计算服务供应商&#xff0c;为WordPress提供了强大且灵活的托管环境&#xff0c;使用AWS来搭建和运行WordPress无疑是个不错的选择。即便如此使用AWS制作还是会有些许利弊&#xff0c;九河云作为AWS的合作伙伴来为读者们仔细探讨AWS在WordPress的利弊。 利&…

diskMirror docker 使用容器部署 diskMirror 服务器!!!

Welcome to diskMirror-docker 获取项目 这个项目是 diskMirror-spring-boot 镜像版本的项目&#xff0c;您可以使用下面的命令将此项目编译为一个镜像&#xff01; # 进入到您下载的源码包目录 cd diskMirror-docker# 点击脚本来进行版本的设置以及对应版本的下载 设置 和 编…

Linux 安装 nvm,并使用 Jenkins 打包前端

文章目录 nvm是什么nvm下载nvm安装设置 nvm 环境变量设置 Jenkins 打包命令 nvm是什么 nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装…

artifactory配置docker本地存储库

​一、概述 本地 Docker 存储库是我们部署和托管内部 Docker 镜像的位置。实际上&#xff0c;它是一个 Docker 注册表&#xff0c;能够托管的 Docker 镜像的集合。通过本地存储库&#xff0c;你可以保存、加载、共享和管理自己的 Docker 镜像&#xff0c;而无需依赖于外部的镜像…