原生JS实现DOM爆炸效果(JavaScript实现随机生成div的效果方法)
原生JS实现DOM爆炸效果
前言
此次分享是一次自我组件开发的总结,还是有很多不足之处,望各位大大多提宝贵意见,互相学习交流。
分享内容介绍
效果预览
效果分析
- * 点击作为动画开始的起点,自动结束* 每次效果产生多个抛物线粒子运动的元素,方向随机,
- 展示内容不一样,有空间上Z轴的大小变化* 需求上可以无间隔点击
- ,即第一组动画未结束可播放第二组动画* 动画基本执行时长一致
由以上四点分析后,动画实现有哪些实现方案呢?
- css操作态变换(如focus)使子元素执行动画
- Js 控制动画开始,事先写好css动画预置,通过class 包含选择器切换动画 例如: .active .items{animation:xxx ...;}
- 事先写好大量动画,隐藏大量dom元素,动画开始随机选取dom元素执行自己唯一的动画keyframes
- 抛弃css动画,使用canvas 绘制动画
- js做dom创建,生成随机css @keyframes
- js 刷帧 做dom渲染
结论
canvas虽说可行,但由于其开发弊端 本次分享不以canvas为分享内容,而是使用最后一种 js刷帧的dom操作
组件结构
由截图分享,动画可以分为两个模块,首先,随机发散的粒子具有共性:抛物线动画,淡出,渲染表情
而例子数量变多之后则为截图中的效果
但是,由于性能原因,我们需要做到粒子的掌控,实现资源再利用,那么还需要第二个模块,作为粒子的管控组件
所以: 此功能可使用两个模块进行开发: partical.js 粒子功能 与 boom.js 粒子管理
实现 Partical.js
1. 前置资源:抛物线运动的物理曲线需要使用Tween.js提供的速度函数
若不想引入Tween.js 可以使用以下代码
/** Tween.js
* t: current time(当前时间);
* b: beginning value(初始值);
* c: change in value(变化量);
* d: duration(持续时间)。
* you can visit \'缓动函数速查表\' to get effect
*/
const Quad = {
easeIn: function(t, b, c, d) {
return c * (t /= d) * t + b;
},
easeOut: function(t, b, c, d) {
return -c *(t /= d)*(t-2) + b;
},
easeInOut: function(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b;
return -c / 2 * ((--t) * (t-2) - 1) + b;
}
}
const Linear = function(t, b, c, d) {
return c * t / d + b;
}
2. 粒子实现
实现思路:
希望在粒子管控组件时,使用new partical的方式创建粒子,每个粒子存在自己的动画开始方法,动画结束回调。
由于评论列表可能存在数量巨大的情况,我们希望只全局创建有限个数的粒子,那么则提供呢容器移除粒子功能以及容器添加粒子的功能,实现粒子的复用
partical_style.css
//粒子充满粒子容器,需要容器存在尺寸以及relative定位
.Boom-Partical_Holder{
position: absolute;
left:0;
right:0;
top:0;
bottom:0;
margin:auto;
}
particle.js
import \"partical_style.css\";
class Partical{
// dom为装载动画元素的容器 用于设置位置等样式
dom = null;
// 动画开始时间
StartTime = -1;
// 当前粒子的动画方向,区别上抛运动与下抛运动
direction = \"UP\";
// 动画延迟
delay = 0;
// 三方向位移值
targetZ = 0;
targetY = 0;
targetX = 0;
// 缩放倍率
scaleNum = 1;
// 是否正在执行动画
animating = false;
// 粒子的父容器,标识此粒子被渲染到那个元素内
parent = null;
// 动画结束的回调函数列表
animEndCBList = [];
// 粒子渲染的内容容器 slot
con = null;
constructor(){
//创建动画粒子dom
this.dom = document.createElement(\"div\");
this.dom.classList.add(\"Boom-Partical_Holder\");
this.dom.innerHTML =
;
<div class=\"Boom-Partical_con\">
Boom
</div>
}
// 在哪里渲染
renderIn(parent) {
// dom判断此处省略
parent.appendChild(this.dom);
this.parent = parent;
// 此处为初始化 slot 容器
!this.con && ( this.con = this.dom.querySelector(\".Boom-Partical_con\"));
}
// 用于父容器移除当前粒子
deleteEl(){
// dom判断此处省略
this.parent.removeChild(this.dom);
}
// 执行动画,需要此粒子执行动画的角度,动画的力度,以及延迟时间
animate({ deg, pow, delay } = {}){
// 后续补全
}
// 动画结束回调存储
onAnimationEnd(cb) {
if (typeof cb !== \'function\') return;
this.animEndCBList.push(cb);
}
// 动画结束回调执行
emitEndCB() {
this.dom.style.cssText += ;-webkit-transform:translate3d(0,0,0);opacity:1;
;
this.animating = false;
try {
for (let cb of this.animEndCBList) {
cb();
}
} catch (error) {
console.warn(\"回调报错:\",cb);
}
}
// 简易实现slot功能,向粒子容器内添加元素
insertChild(child){
this.con.innerHTML = \'\';
this.con.appendChild(child);
}
}
致此,我们先创建了一个粒子对象的构造函数,现在考虑一下我们实现了我们的设计思路吗?
- * 使用构造函数new Partical( )粒子* 粒子实力对象存在 animate 执行动画方法*
- 有动画结束回调函数的存储和执行*
- 设置粒子的父元素: renderIn 方法* 父元素删除粒子: deleteEl 方法
为了更好的展示粒子内容,我们特意在constructor里创建了一个 Boom-Partical_con 元素用于模拟slot功能: insertChild方法,用于使用者展示不同的内容进行爆炸
接下来考虑一下动画的实现过程,动画毫无疑问为抛物线动画,这种动画在代码中实现可以使用物理公式,
但是我们也可以通过速度曲线实现,想想上抛过程可以想成 由于重力影响 ,变成一个速度逐渐减小的向上位移的过程,
而下抛过程可以理解为加速过程;
则可对应为速度曲线的easeOut 与 easeIn,
而水平方向可以理解为匀速运动,则是 linear;
我们以水平向右为X正方向0度,顺时针方向角度增加;
则 小于 180度为向下, 大于180度为向上
假设方向为四点钟
方向,夹角则为 30
度,
按照高中物理,大小为N的力:
力的分解图解
也就是说 我们可以知道一个方向上的力在XY轴的分量大小,
假设我们将 力 的概念 转化为 视图中 位移的概念,
我们将 力量1 记为 10vh的大小
于是我们可以定义全局变量
const POWER = 10; // 单位 vh 力的单位转化比例
const G = 5; // 单位 vh 重力值
const DEG = Math.PI / 180;
const Duration = .4e3; //假设动画执行时长400毫秒
由此 我们补全 animate方法
// 执行动画 角度 , 力 1 ~ 10 ; 1 = 10vh
animate({ deg, pow, delay } = {}) {
this.direction = deg > 180 ? \"UP\" : \"DOWN\";
this.delay = delay || 0;
let r = Math.random();
this.targetZ = 0;
this.targetY = Math.round(pow * Math.sin(deg * DEG) * POWER);
this.targetX = Math.round(pow * Math.cos(deg * DEG) * POWER) * (r + 1);
this.scaleNum = (r * 0.8) * (r < 0.5 ? -1 : 1);
this.raf();
}
animte的思路为:通过传入的角度和力度 计算目标终点位置(因为力最终转化为位移值,力越大,目标位移越大)
使用随机数计算此次动画的缩放值变化范围(-0.8 ~ 0.8)
然后执行刷帧操作 raf
raf(){
// 正在执行动画
this.animating = true;
// 动画开始时间
this.StartTime = +new Date();
let StartTime = this.StartTime;
// 获取延时
let delay = this.delay;
// 动画会在延时后开始,也就是真正开始动画的时间
let StartTimeAfterDelay = StartTime + delay
let animate = () => {
// 获取从执行动画开始经过了多久
let timeGap = +new Date() - StartTimeAfterDelay;
// 大于0 证明过了delay时间
if (timeGap >= 0) {
// 大于Duration证明过了结束时间
if (timeGap > Duration) {
// 执行动画结束回调
this.emitEndCB();
return;
}
// 设置应该设置的位置的样式
this.dom.style.cssText += ;will-change:transform;-webkit-transform:translate3d(${this.moveX(timeGap)}vh,${this.moveY(timeGap)}vh,0) scale(${this.scale(timeGap)});opacity:${this.opacity(timeGap)};
;
}
requestAnimationFrame(animate);
}
animate();
}
刷帧操作中判断了delay时间的处理以及结束的时间处理回调
那么揭晓来就剩下 moveX,moveY,scale,opacity的设置
// 水平方向为匀速,所以使用Linear
moveX(currentDuration) {
// 此处 * 2 是效果矫正后的处理,可根据自己的需求修改水平位移速度
return Linear(currentDuration, 0, this.targetX, Duration) * 2;
}
// 缩放 使用了easeOut曲线, 可根据需求自行修改
scale(currentDuration) {
return Quad.easeOut(currentDuration, 1, this.scaleNum, Duration);
}
// 透明度 使用了easeIn速度曲线,保证后消失
opacity(currentDuration) {
return Quad.easeIn(currentDuration, 1, -1, Duration);
}
// 竖直方向上位移计算
moveY(currentDuration) {
let direction = this.direction;
if (direction === \'UP\') {
// G用于模拟上抛过程的重力
// 如果是上抛运动
if (currentDuration < Duration / 2) {
// 上抛过程 我们使用easeOut速度逐渐减小,我们让动画在一半时移到最高点
return Quad.easeOut(currentDuration, 0, this.targetY + G, Duration / 2);
}
// 上抛的下降过程,从最高点下降
return this.targetY + G - Quad.easeIn(currentDuration - Duration / 2, 0, this.targetY / 2, Duration / 2);
}
// 下抛运动直接easeIn
return Quad.easeIn(currentDuration, 0, this.targetY, Duration);
}
至此,partical.js 结束,文件末尾加一行
export default Partical;
此时 我们的partical.js输出一个构造函数:
- * new 的时候创建了粒子元素,* 使用onAnimtionEnd可以实现动画结束的回调函数* insertChild可以向粒子内渲染使用者自定义的dom* renderIn 可以设置粒子父元素* deleteEl 可以从父元素删除粒子* animate 可以执行刷帧,渲染计算位置,触发回调
于是对于粒子来说,只剩下在执行animte的时候 传入的力的大小,方向,以及延迟时间
粒子管理 Boom.js
对于Boom.js的功能需求为
- 创建粒子执行粒子动画,赋予动画力、角度、延时设置粒子容器
可达到效果:
- 不关心业务,业务使用者传入每个粒子slot内容数组粒子组件可复用易于维护(可能是哈哈哈)
于是粒子管理器构架为:
import Partical from \"partical.js\";
class Boom{
// 实例化的粒子列表
particalList = [];
// 单次生成的粒子个数
particalNumbers = 6;
// 执行动画的间隔时间
boomTimeGap = .1e3;
boomTimer = 0;
// 用户插入粒子的slot 的内容
childList = [];
// 默认旋转角度
rotate = 120;
// 默认的粒子发散范围
spread = 180;
// 默认随机延迟范围
delayRange = 100;
// 默认力度
power = 3;
// 此次执行粒子爆炸的是那个容器
con = null;
constructor({ childList , container , boomNumber , rotate , spread , delayRange , power} = {}){
this.childList = childList || [];
this.con = container || null;
this.particalNumbers = boomNumber || 6;
this.rotate = rotate || 120;
this.spread = spread || 180;
this.delayRange = delayRange || 100;
this.power = power || 3;
this.createParticals(this.particalNumbers);
}
setContainer(con){
this.con = con;
}
// 创建粒子 存入内存数组中
createParticals(num){
for(let i = 0 ; i < num ; i++){
let partical = new Partical();
partical.onAnimationEnd(()=>{
partical.deleteEl();
});
this.particalList.push(partical)
}
}
// 执行动画
boom(){
// 限制动画执行间隔
let lastBoomTimer = this.boomTimer;
let now = +new Date();
if(now - lastBoomTimer < this.boomTimeGap){
// console.warn(\"点的太快了\");
return;
}
this.boomTimer = now;
console.warn(\"粒子总数:\" , this.particalList.length)
let boomNums = 0;
// 在内存列表找,查找没有执行动画的粒子
let unAnimateList = this.particalList.filter(partical => partical.animating == false);
let childList = this.childList;
let childListLength = childList.length;
let rotate = this.rotate;
let spread = this.spread;
let delayRange = this.delayRange;
let power = this.power;
// 每有一个未执行动画的粒子,执行一次动画
for(let partical of unAnimateList){
if(boomNums >= this.particalNumbers) return ;
boomNums++;
let r = Math.random();
// 设置粒子父容器
partical.renderIn(this.con);
// 随机选择粒子的slot内容
partical.insertChild(childList[Math.floor(r * childListLength)].cloneNode(true));
// 执行动画,在输入范围内随机角度、力度、延迟
partical.animate({
deg: (r * spread + rotate) % 360,
pow: r * power + 1,
delay: r * delayRange,
});
}
// 如果粒子树木不够,则再次创建,防止下次不够用
if(boomNums < this.particalNumbers){
this.createParticals(this.particalNumbers - boomNums);
}
}
}
export default Boom;
使用demo
let boomChildList = [];
for(let i = 0 ; i < 10; i++){
let tempDom = document.createElement(\"div\");
tempDom.className = \"demoDom\";
tempDom.innerHTML = i;
boomChildList.push(tempDom);
}
let boom = new Boom({
childList: boomChildList,
boomNumber: 6,
rotate: 0,
spread: 360,
delayRange: 100,
power: 3,
});
组件效果预览
结尾
可能效果中实现的思维还有不妥和欠缺,欢迎各位大大提出宝贵意见,互相交流、学习!
原文链接:https://zhuanlan.zhihu.com/p/47770130
JavaScript实现随机生成div的效果方法
鼠标点击页面,在哪里点击就在那个位置创建一个div,宽高50,颜色随机,div在鼠标点击的正中间。今天小编就分享一个JavaScript实现随机生成div的效果方法。
效果如下图所示:
js代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
var Method=(function () { return { EVENT_ID:\"event_id\", loadImage:function (arr) { var img=new Image(); img.arr=arr; img.list=[]; img.num=0; // 如果DOM对象下的事件侦听没有被删除掉,将会常驻堆中 // 一旦触发了这个事件需要的条件,就会继续执行事件函数 img.addEventListener(\"load\",this.loadHandler); img.self=this; img.src=arr[img.num]; }, loadHandler:function (e) { this.list.push(this.cloneNode(false)); this.num++; if(this.num>this.arr.length-1){ this.removeEventListener(\"load\",this.self.loadHandler); var evt=new Event(Method.EVENT_ID); evt.list=this.list; document.dispatchEvent(evt); return; } this.src=this.arr[this.num]; }, $c:function (type,parent,style) { var elem=document.createElement(type); if(parent) parent.appendChild(elem); for(var key in style){ elem.style[key]=style[key]; } return elem; }, divColor: function () { var col=\"#\";//这个字符串第一位为# 颜色的格式 for(var i=0;i<6;i++){ col+=parseInt(Math.random()*16).toString(16);//rondom*16后的随机值即为0-1*16==0-16; toString(16)为转化为16进制 } return col;//最后返回一个七位的值 格式即为#nnnnnn 颜色的格式 }, random:function (min,max) { max=Math.max(min,max); min=Math.min(min,max); return Math.floor(Math.random()*(max-min)+min); }, dragElem:function (elem) { elem.addEventListener(\"mousedown\",this.mouseDragHandler); elem.self=this; }, removeDrag:function (elem) { elem.removeEventListener(\"mousedown\",this.mouseDragHandler); }, mouseDragHandler:function (e) { if(e.type===\"mousedown\"){ e.stopPropagation(); e.preventDefault(); document.point={x:e.offsetX,y:e.offsetY}; document.elem=this; this.addEventListener(\"mouseup\",this.self.mouseDragHandler); document.addEventListener(\"mousemove\",this.self.mouseDragHandler); }else if(e.type===\"mousemove\"){ this.elem.style.left=e.x-this.point.x+\"px\"; this.elem.style.top=e.y-this.point.y+\"px\"; }else if(e.type===\"mouseup\"){ this.removeEventListener(\"mouseup\",this.self.mouseDragHandler); document.removeEventListener(\"mousemove\",this.self.mouseDragHandler); } } } })(); |
html代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html> <head> <meta charset=\"UTF-8\"> <title>Title</title> </head> <script src=\"js/Method.js\"></script> <body> <script> init(); function init() { document.addEventListener(\"mousedown\",mouseHandler); } function mouseHandler(e) { var randomDiv=Method.$c(\"div\",document.body,{ width: \"50px\", height: \"50px\", position: \"absolute\", backgroundColor:divColor() }) randomDiv.style.left=e.clientX-randomDiv.offsetWidth/2+\"px\"; randomDiv.style.top=e.clientY-randomDiv.offsetHeight/2+\"px\"; /* top:e.clientY-this.offsetHeight/2+\"px\",//原因 设置为了X...xbl // removeEventListener(randomDiv);*/ } function divColor() { var col=\"#\";//这个字符串第一位为# 颜色的格式 for(var i=0;i<6;i++){ col+=parseInt(Math.random()*16).toString(16);//rondom*16后的随机值即为0-1*16==0-16; toString(16)为转化为16进制 } return col;//最后返回一个七位的值 格式即为#nnnnnn 颜色的格式 } </script> </body> </html> |
以上就是JavaScript实现随机生成div的效果方法的详细内容。
1. 带 [亲测] 说明源码已经被站长亲测过!
2. 下载后的源码请在24小时内删除,仅供学习用途!
3. 分享目的仅供大家学习和交流,请不要用于商业用途!
4. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
5. 本站所有资源来源于站长上传和网络,如有侵权请邮件联系站长!
6. 没带 [亲测] 代表站长时间紧促,站长会保持每天更新 [亲测] 源码 !
7. 盗版ripro用户购买ripro美化无担保,若设置不成功/不生效我们不支持退款!
8. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
9. 如果你也有好源码或者教程,可以到审核区发布,分享有金币奖励和额外收入!
10.如果您购买了某个产品,而我们还没来得及更新,请联系站长或留言催更,谢谢理解 !
GG资源网 » 原生JS实现DOM爆炸效果(JavaScript实现随机生成div的效果方法)