你永远不知道前端大神们能玩儿出哪些让你看一眼就惊掉下巴,瞪掉眼珠子的特效。更神奇的是,看似复杂的效果,其实背后的代码远比你想象的要简单。上周公司团建,小编去感受了一把重力加速度的刺激(蹦!极!),吓die了好吗,于是乎小编跟重力杠上了,正好看到了几个重力模拟的特效,分享给大家,妈妈问我为什么跪着看完这篇文章,等你看完了也就知道了。。。。。。。
Gravity Test
这个特效模拟了泡泡球从天而降的效果,想必大家小时候都这样玩过泡泡球吧,使劲往地上扔然后看球弹起来,这个特效绝对满足你~
代码:
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
height: 100%;
overflow: hidden;
font-family: 'Source Code Pro';
}
h1 {
position: absolute;
top: 10vh;
left: 0;
right: 0;
z-index: 2;
color: #222;
font-size: 8vw;
text-align: center;
}
canvas {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
</style>
</head>
<body>
<h1>Gravity Test</h1>
<canvas id="canvas"></canvas>
<script>
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function(object, eventType, callback){
var timer;
object.addEventListener(eventType, function(event) {
clearTimeout(timer);
timer = setTimeout(function(){
callback(event);
}, 500);
}, false);
};
},{}],2:[function(require,module,exports){
var Vector2 = require('./vector2');
var exports = {
friction: function(vector, value) {
var force = vector.clone();
force.multScalar(-1);
force.normalize();
force.multScalar(value);
return force;
},
drag: function(vector, value) {
var force = vector.clone();
force.multScalar(-1);
force.normalize();
force.multScalar(vector.length() * value);
return force;
},
hook: function(v_velocity, v_anchor, k) {
var force = v_velocity.clone().sub(v_anchor);
var distance = force.length();
if (distance > 0) {
force.normalize();
force.multScalar(-1 * k * distance);
return force;
} else {
return new Vector2();
}
}
};
module.exports = exports;
},{"./vector2":6}],3:[function(require,module,exports){
var Util = require('./util');
var Vector2 = require('./vector2');
var Force = require('./force');
var Mover = require('./mover');
var debounce = require('./debounce');
var body_width = document.body.clientWidth * 2;
var body_height = document.body.clientHeight * 2;
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var last_time_activate = Date.now();
var vector_touch_start = new Vector2();
var vector_touch_move = new Vector2();
var vector_touch_end = new Vector2();
var is_touched = false;
var movers = [];
var count_movers = 0;
var unit_mover = 300;
var gravity = new Vector2(0, 1);
var init = function() {
poolMover();
renderloop();
setEvent();
resizeCanvas();
debounce(window, 'resize', function(event){
resizeCanvas();
});
};
var poolMover = function () {
for (var i = 0; i < unit_mover; i++) {
var mover = new Mover();
movers.push(mover);
}
count_movers += unit_mover;
};
var updateMover = function () {
for (var i = 0; i < movers.length; i++) {
var mover = movers[i];
if (!mover.is_active) continue;
if (mover.acceleration.length() < 2) {
mover.time ++;
}
if (mover.time > 20) {
mover.radius -= mover.radius / 10;
}
if (mover.radius < 10) {
mover.inactivate();
continue;
}
mover.applyForce(gravity);
mover.applyFriction();
mover.updateVelocity();
collideMover(mover, i, movers, true);
mover.collideBorder(false, body_width, body_height, 0, true);
collideMover(mover, i, movers, false);
collideMover(mover, i, movers, false);
collideMover(mover, i, movers, false);
mover.updatePosition();
movers[i].draw(ctx);
}
};
var collideMover = function(mover, i, movers, preserve_impulse) {
for (var index = 0; index < movers.length; index++) {
if (index === i) continue;
mover.collide(movers[index], preserve_impulse);
}
}
var activateMover = function () {
var vector = new Vector2(Util.getRandomInt(0, body_width), body_height / 2 * -1);
var radian = 0;
var scalar = 0;
var x = 0;
var y = 0;
var force = new Vector2();
for (var i = 0; i < movers.length; i++) {
var mover = movers[i];
if (mover.is_active) continue;
mover.activate();
mover.init(vector, (body_width + body_height) / 200);
break;
}
};
var render = function() {
ctx.clearRect(0, 0, body_width, body_height);
updateMover();
};
var renderloop = function() {
var now = Date.now();
requestAnimationFrame(renderloop);
render();
if (now - last_time_activate > 10) {
activateMover();
last_time_activate = Date.now();
}
};
var resizeCanvas = function() {
body_width = document.body.clientWidth * 2;
body_height = document.body.clientHeight * 2;
canvas.width = body_width;
canvas.height = body_height;
canvas.style.width = body_width / 2 + 'px';
canvas.style.height = body_height / 2 + 'px';
};
var setEvent = function () {
var eventTouchStart = function(x, y) {
vector_touch_start.set(x, y);
is_touched = true;
};
var eventTouchMove = function(x, y) {
vector_touch_move.set(x, y);
if (is_touched) {
}
};
var eventTouchEnd = function(x, y) {
vector_touch_end.set(x, y);
is_touched = false;
};
canvas.addEventListener('contextmenu', function (event) {
event.preventDefault();
});
canvas.addEventListener('selectstart', function (event) {
event.preventDefault();
});
canvas.addEventListener('mousedown', function (event) {
event.preventDefault();
eventTouchStart(event.clientX * 2, event.clientY * 2);
});
canvas.addEventListener('mousemove', function (event) {
event.preventDefault();
eventTouchMove(event.clientX * 2, event.clientY * 2);
});
canvas.addEventListener('mouseup', function (event) {
event.preventDefault();
eventTouchEnd();
});
canvas.addEventListener('touchstart', function (event) {
event.preventDefault();
eventTouchStart(event.touches[0].clientX * 2, event.touches[0].clientY * 2);
});
canvas.addEventListener('touchmove', function (event) {
event.preventDefault();
eventTouchMove(event.touches[0].clientX * 2, event.touches[0].clientY * 2);
});
canvas.addEventListener('touchend', function (event) {
event.preventDefault();
eventTouchEnd();
});
};
init();
},{"./debounce":1,"./force":2,"./mover":4,"./util":5,"./vector2":6}],4:[function(require,module,exports){
var Util = require('./util');
var Vector2 = require('./vector2');
var Force = require('./force');
var exports = function(){
var Mover = function() {
this.position = new Vector2();
this.velocity = new Vector2();
this.acceleration = new Vector2();
this.anchor = new Vector2();
this.radius = 0;
this.mass = 1;
this.direction = 0;
this.r = Util.getRandomInt(200, 255);
this.g = Util.getRandomInt(0, 180);
this.b = Util.getRandomInt(0, 50);
this.a = 1;
this.time = 0;
this.is_active = false;
};
Mover.prototype = {
init: function(vector, size) {
this.radius = Util.getRandomInt(size, size * 4);
this.mass = this.radius / 100;
this.position = vector.clone();
this.velocity = vector.clone();
this.anchor = vector.clone();
this.acceleration.set(0, 0);
this.a = 1;
this.time = 0;
},
updatePosition: function() {
this.position.copy(this.velocity);
},
updateVelocity: function() {
this.velocity.add(this.acceleration);
if (this.velocity.distanceTo(this.position) >= 1) {
this.direct(this.velocity);
}
},
applyForce: function(vector) {
this.acceleration.add(vector);
},
applyFriction: function() {
var friction = Force.friction(this.acceleration, 0.1);
this.applyForce(friction);
},
applyDragForce: function() {
var drag = Force.drag(this.acceleration, 0.5);
this.applyForce(drag);
},
hook: function() {
var force = Force.hook(this.velocity, this.anchor, this.k);
this.applyForce(force);
},
rebound: function(vector, e) {
var dot = this.acceleration.clone().dot(vector);
this.acceleration.sub(vector.multScalar(2 * dot));
this.acceleration.multScalar(e);
},
direct: function(vector) {
var v = vector.clone().sub(this.position);
this.direction = Math.atan2(v.y, v.x);
},
collide: function(target, preserve_impulse) {
var distance = this.velocity.distanceTo(target.velocity);
var rebound_distance = this.radius + target.radius;
var damping = 0.9;
if (distance < rebound_distance) {
var overlap = Math.abs(distance - rebound_distance);
var this_normal = this.velocity.clone().sub(target.velocity).normalize();
var target_normal = target.velocity.clone().sub(this.velocity).normalize();
this.velocity.sub(target_normal.clone().multScalar(overlap / 2));
target.velocity.sub(this_normal.clone().multScalar(overlap / 2));
if(preserve_impulse){
var scalar1 = target.acceleration.length();
var scalar2 = this.acceleration.length();
this.acceleration.sub(this_normal.multScalar(scalar1 / -2)).multScalar(damping);
target.acceleration.sub(target_normal.multScalar(scalar2 / -2)).multScalar(damping);
if (Math.abs(this.acceleration.x) < 1) this.acceleration.x = 0;
if (Math.abs(this.acceleration.y) < 1) this.acceleration.y = 0;
if (Math.abs(target.acceleration.x) < 1) target.acceleration.x = 0;
if (Math.abs(target.acceleration.y) < 1) target.acceleration.y = 0;
}
}
},
collideBorder: function(top, right, bottom, left, preserve_impulse) {
var damping = 0.6;
if (top !== false && this.position.y - this.radius < top) {
var normal = new Vector2(0, 1);
this.velocity.y = this.radius;
if (preserve_impulse) this.acceleration.y *= -1 * damping;
}
if (right !== false && this.position.x + this.radius > right) {
var normal = new Vector2(-1, 0);
this.velocity.x = right - this.radius;
if (preserve_impulse) this.acceleration.x *= -1 * damping;
}
if (bottom !== false && this.position.y + this.radius > bottom) {
var normal = new Vector2(0, -1);
this.velocity.y = bottom - this.radius;
if (preserve_impulse) this.acceleration.y *= -1 * damping;
}
if (left !== false && this.position.x - this.radius < left) {
var normal = new Vector2(1, 0);
this.velocity.x = this.radius;
if (preserve_impulse) this.acceleration.x *= -1 * damping;
}
},
draw: function(context) {
context.fillStyle = 'rgba(' + this.r + ',' + this.g + ',' + this.b + ',' + this.a + ')';
context.beginPath();
context.arc(this.position.x, this.position.y, this.radius, 0, Math.PI / 180, true);
context.fill();
},
activate: function () {
this.is_active = true;
},
inactivate: function () {
this.is_active = false;
}
};
return Mover;
};
module.exports = exports();
},{"./force":2,"./util":5,"./vector2":6}],5:[function(require,module,exports){
var exports = {
getRandomInt: function(min, max){
return Math.floor(Math.random() * (max - min)) + min;
},
getDegree: function(radian) {
return radian / Math.PI * 180;
},
getRadian: function(degrees) {
return degrees * Math.PI / 180;
},
getSpherical: function(rad1, rad2, r) {
var x = Math.cos(rad1) * Math.cos(rad2) * r;
var z = Math.cos(rad1) * Math.sin(rad2) * r;
var y = Math.sin(rad1) * r;
return [x, y, z];
}
};
module.exports = exports;
},{}],6:[function(require,module,exports){
//
// このVector2クラスは、three.jsのTHREE.Vector2クラスの計算式の一部を利用しています。
// https://github.com/mrdoob/three.js/blob/master/src/math/Vector2.js#L367
//
var exports = function(){
var Vector2 = function(x, y) {
this.x = x || 0;
this.y = y || 0;
};
Vector2.prototype = {
set: function (x, y) {
this.x = x;
this.y = y;
return this;
},
copy: function (v) {
this.x = v.x;
this.y = v.y;
return this;
},
add: function (v) {
this.x += v.x;
this.y += v.y;
return this;
},
addScalar: function (s) {
this.x += s;
this.y += s;
return this;
},
sub: function (v) {
this.x -= v.x;
this.y -= v.y;
return this;
},
subScalar: function (s) {
this.x -= s;
this.y -= s;
return this;
},
mult: function (v) {
this.x *= v.x;
this.y *= v.y;
return this;
},
multScalar: function (s) {
this.x *= s;
this.y *= s;
return this;
},
div: function (v) {
this.x /= v.x;
this.y /= v.y;
return this;
},
divScalar: function (s) {
this.x /= s;
this.y /= s;
return this;
},
min: function (v) {
if ( this.x < v.x ) this.x = v.x;
if ( this.y < v.y ) this.y = v.y;
return this;
},
max: function (v) {
if ( this.x > v.x ) this.x = v.x;
if ( this.y > v.y ) this.y = v.y;
return this;
},
clamp: function (v_min, v_max) {
if ( this.x < v_min.x ) {
this.x = v_min.x;
} else if ( this.x > v_max.x ) {
this.x = v_max.x;
}
if ( this.y < v_min.y ) {
this.y = v_min.y;
} else if ( this.y > v_max.y ) {
this.y = v_max.y;
}
return this;
},
floor: function () {
this.x = Math.floor( this.x );
this.y = Math.floor( this.y );
return this;
},
ceil: function () {
this.x = Math.ceil( this.x );
this.y = Math.ceil( this.y );
return this;
},
round: function () {
this.x = Math.round( this.x );
this.y = Math.round( this.y );
return this;
},
roundToZero: function () {
this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
return this;
},
negate: function () {
this.x = - this.x;
this.y = - this.y;
return this;
},
dot: function (v) {
return this.x * v.x + this.y * v.y;
},
lengthSq: function () {
return this.x * this.x + this.y * this.y;
},
length: function () {
return Math.sqrt(this.lengthSq());
},
normalize: function () {
return this.divScalar(this.length());
},
distanceTo: function (v) {
var dx = this.x - v.x;
var dy = this.y - v.y;
return Math.sqrt(dx * dx + dy * dy);
},
setLength: function (l) {
var oldLength = this.length();
if ( oldLength !== 0 && l !== oldLength ) {
this.multScalar(l / oldLength);
}
return this;
},
clone: function () {
return new Vector2(this.x, this.y);
}
}
return Vector2;
};
module.exports = exports();
},{}]},{},[3])
</script>
</body>
</html>
Gravity balls to mouse study
如果说上面这个特效还不够生动的话,那么这个特效将会更3D的呈现效果,并且小球的位置还会根据你的鼠标位置改变哟。
代码:
<html>
<head>
<style>
body {
margin: 0;
overflow: hidden;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
// Initial Setup
const canvas = document.querySelector('canvas');
const c = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
// Variables
let mouse = {
x: innerWidth / 2,
y: innerHeight / 2
};
let ballCount = 750;
let balls = [];
let gravityPos = [];
let friction = .995;
let explosionDistance = 2;
let shouldExplode = false;
const colors = [
'#81C3D7',
'#D9DCD6',
'#3A7CA5',
'#2F6690'
];
const bgColor = '#16425B';
// Event Listeners
addEventListener("mousemove", function(event) {
mouse.x = event.clientX;
mouse.y = event.clientY;
gravityPos = [mouse.x, mouse.y];
});
addEventListener("mouseout", function(event) {
gravityPos = [canvas.width / 2, canvas.height / 2];
});
addEventListener("resize", function() {
canvas.width = innerWidth;
canvas.height = innerHeight;
init();
});
addEventListener("click", function() {
init();
});
// Utility Functions
function randomIntFromRange(min,max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function randomeFloatFromRange(min, max){
return Math.random() * (max - min) + min;
}
function randomColor(colors) {
return colors[Math.floor(Math.random() * colors.length)];
}
// Objects
function Ball(px, py, vx, vy, f, radius, color) {
this.p = [px, py];
this.v = [vx, vy];
this.gv = [0, 0];
this.gp = 0;
this.radius = radius;
this.color = color;
this.f = f;
this.update = function() {
// calculate gravity vector
this.gv = [gravityPos[0] - this.p[0], gravityPos[1] - this.p[1]];
// Calculate gravity intensity
let a = gravityPos[0] - this.p[0];
let b = gravityPos[1] - this.p[1];
this.gp = 1 / (Math.sqrt( a*a + b*b ));
// Explode if needed
if (shouldExplode){
this.v[0] *= randomeFloatFromRange(-10, 10);
this.v[1] *= randomeFloatFromRange(-10, 10);
}
// Reduce ball's own velocity with friction
this.v[0] *= this.f;
this.v[1] *= this.f;
// Calculate new velocity, add gravity
this.v[0] += this.gv[0] * this.gp * this.f;
this.v[1] += this.gv[1] * this.gp * this.f;
// Move
this.p[0] += this.v[0];
this.p[1] += this.v[1];
this.draw();
};
this.draw = function() {
c.save();
c.beginPath();
c.arc(this.p[0], this.p[1], this.radius, 0, Math.PI * 2, false);
c.fillStyle = this.color;
c.fill();
c.closePath();
c.restore();
};
}
// Implementation
function init() {
gravityPos = [canvas.width / 2, canvas.height / 2];
balls = [];
for(let i = 0 ; i < ballCount ; i++){
let rd = randomeFloatFromRange(1, 7);
let px = randomeFloatFromRange(0, canvas.width / 3) + (canvas.width / 3);
let py = randomeFloatFromRange(0, canvas.height / 3) + (canvas.height / 3);
let vx = randomeFloatFromRange(-10, 10);
let vy = randomeFloatFromRange(-10, 10);
let f = friction;
balls.push(new Ball(px, py, vx, vy, f, rd, randomColor(colors)));
}
}
// Animation Loop
function animate() {
requestAnimationFrame(animate);
resetCanvas(bgColor);
updateShouldExplode();
for(let i = 0 ; i < balls.length ; i++){
balls[i].update();
}
}
init();
animate();
function resetCanvas(color){
if(color){
c.save();
c.fillStyle = color;
c.fillRect(0, 0, canvas.width, canvas.height);
c.restore();
}else{
c.clearRect(0, 0, canvas.width, canvas.height);
}
}
function updateShouldExplode(){
let x = 0;
let y = 0;
for(let i = 0 ; i < balls.length ; i++){
x += balls[i].v[0] < 0 ? balls[i].v[0] * -1 : balls[i].v[0];
y += balls[i].v[1] < 0 ? balls[i].v[1] * -1 : balls[i].v[1];
}
shouldExplode = x / balls.length < explosionDistance && y / balls.length < explosionDistance;
}
</script>
</body>
</html>
更多好玩儿的效果请点击这里:http://igeekbar.com/igeekbar/post/246.htm
相关推荐
本资源“纯CSS3实现11种超酷Loading动画加载指示器特效源码.zip”提供了一套完整的解决方案,旨在帮助开发者创建吸引人的、具有动态效果的加载指示器,以提升用户体验。 1. CSS3 动画基础: CSS3引入了关键帧动画...
标题中的“跨年烟花代码(进阶版)”指的是一个前端开发项目,目的是在网页上实现动态的烟花绽放效果,通常用于庆祝新年或特殊节日,如描述中提到的圣诞节。这个项目是一个进阶版本,意味着它可能包含了更复杂的动画...
对于IE6,由于其不支持CSS的`:hover`伪类应用在非链接元素上,我们需要引入条件注释或者使用JavaScript/jQuery来模拟这个功能。例如: ```html <!--[if lt IE 7]> document.createElement('div'); <![endif]--> ...
在“JS特效大全网站”中,你将找到实现这些效果的源代码和详细教程,涵盖基础到进阶的JavaScript知识,包括DOM操作、时间函数、事件处理以及CSS与JavaScript的协同工作等。通过学习和实践这些特效,你可以提升网页...
- **CSS3**:一种层叠样式表,用于定义网页布局和样式,包括过渡、动画和3D变换,与JavaScript结合能实现复杂的特效。 - **jQuery**:一个流行的JavaScript库,简化了DOM操作、事件处理和动画制作,使得编写相册...
jQuery手风琴特效是一种基于jQuery库的交互效果,其名称来源于音乐乐器手风琴的收放动作,形象地模拟了面板的展开和收缩。这种特效通常用于导航菜单、FAQs或任何需要分段显示大量内容的场景,它允许用户逐个查看或...
此时,可以使用JavaScript作为备选方案,为不支持CSS3动画的浏览器提供模拟的滚动效果。同时,注意适时停止或清除定时器,避免资源浪费。 五、实际应用与案例分析 跑马灯特效在新闻网站、广告展示、公告栏等多种...
今天我们要深入探讨的是一个CSS中的特效属性——`text-shadow`,它能为网页的文字添加阴影效果,增加视觉层次感和艺术感。在"Web-前端html+css从入门到精通 158. textshadow文字阴影.zip"这个压缩包中,包含了关于`...
四、优化与进阶 在实际应用中,可以结合其他jQuery插件或CSS3特性,进一步提升用户体验。例如,利用jQuery的事件监听功能,当用户翻页时触发特定的操作;或者利用CSS3的动画效果,为翻页过程添加更多视觉惊喜。 ...
负冲效果是一种模拟传统胶片冲洗过程中出现错误的效果,常用于创造出独特的视觉风格。在Photoshop中,可以通过调整色彩曲线、反相和色彩平衡等工具来实现。首先,复制原图层以保留原始数据,然后调整曲线使颜色偏移...
此外,CSS的box-shadow可以模拟烟花爆炸时的光晕,增加立体感。 在实际应用中,这个烟花特效可能还会涉及到事件监听、DOM操作、动画帧更新等方面的知识。例如,你可以监听用户的鼠标点击事件,让烟花在鼠标所在的...
结合CSS3的动画和过渡效果,以及WebGL等现代图形技术,可以创建更为复杂的3D鼠标跟随特效,如粒子系统、矢量场引导等。 6. **学习资源** 学习和实践这些特效可以通过查阅MDN文档、在线教程、GitHub上的开源项目,...
10. 模仿CSS3动画:对于不支持CSS3的浏览器,JavaScript可以模拟类似效果,如渐变、旋转等。 11. 弹性布局:通过Flexbox或Grid布局,JavaScript可以动态调整元素尺寸和排列,适应不同屏幕尺寸。 12. 无限滚动:当...
轮播图的基本原理是通过改变图片或内容的显示状态,模拟出平滑过渡的效果。通常使用JavaScript来控制定时器,每隔一定时间自动切换到下一张图片,同时提供手动切换选项,如左右箭头或导航点。CSS用于样式设计,使...
图片打碎后组合特效的基本原理是将一个完整的图片分割成多个小块(通常为矩形或正方形),然后通过JavaScript操作这些小块的显示与隐藏,模拟出图片被打碎和重新组合的过程。这一过程中,关键在于如何准确地控制每个...
3.2 文字实例一:模拟Google公司Logo 3.3 文字实例二:制作页面的五彩标题 3.4 CSS段落文字 3.5 段落实例:百度搜索 第4章 用CSS设置图片效果 4.1 图片样式 4.2 图片的对齐 4.3 ...
总的来说,这个压缩包是JavaScript初学者和进阶者实践和学习按钮特效的宝贵资源。通过研究和模仿其中的代码,你可以加深对JavaScript事件处理、DOM操作以及动画原理的理解,并能灵活地应用到自己的项目中,为用户...
总的来说,这个“纯HTML5实现简单气球大战小游戏特效源码”项目是一个绝佳的学习资源,涵盖了HTML5游戏开发的基础和进阶技术,对于想深入了解HTML5游戏开发的开发者来说,无疑是宝贵的实践材料。通过分析和研究这个...
打开窗口向右移动的效果通常用于模拟窗口或对话框的滑动动画,比如加载新内容、弹出通知等。这个效果主要依赖于JavaScript的时间间隔函数(setInterval)和CSS的位置更新。 首先,我们需要在HTML文件(在这个例子中...
在JavaScript中,可以使用requestAnimationFrame来实现平滑的动画帧,通过定时更新元素属性来模拟火焰燃烧的过程,例如改变火焰的颜色、形状和大小,以创造出火焰逐渐升腾、扩散和熄灭的视觉效果。 点击触发的交互...