文章目录
- 1、效果预览
- 2、代码
- 2.1、定义数组写下祝福语
- 2.2、模拟雪花落下的效果
- 2.3、设置背景粒子
- 2.4、操作动画效果
- 2.5、定义闪烁效果
- 2.6、定义粒子对象
- 2.7、粒子对象播放
- 2.8、绘制星星
- 2.9、绘制圣诞树
- 2.10、绘制星星背景动画
- 2.11、定义初始化函数并调用
- 3、结尾
1、效果预览
圣诞
2、代码
2.1、定义数组写下祝福语
分析
定义了一个名为myLabels
的数组,其中包含了三个字符串元素,分别是“Merry Christmas”, “健健康康,平安喜乐”, “一定要站在你所热爱的世界里闪闪发光”。
代码
let myLabels = ["Merry Christmas","健健康康,平安喜乐","一定要站在你所热爱的世界里闪闪发光",
];
2.2、模拟雪花落下的效果
分析
创建了一个<div>
元素,用于表示雪花,设置其样式为绝对定位,颜色为白色,并设置雪花字符为 “❆”。
然后获取了页面的高度和宽度,用于确定雪花的起始和结束位置。
接下来,定义了生成一片雪花的毫秒数,以及一个定时器,定期生成一片雪花。
在每个定时器回调函数中,随机生成雪花的起始和结束位置、大小、持续时间、透明度等参数。
然后,克隆出一个雪花,并根据上述参数设置其样式,并插入到页面中。
接下来设置了一个一次性定时器,用于在雪花生成后一段时间后,修改雪花的样式,使其具有下落效果。
最后,在雪花落下后一段时间,再次设置一个定时器,将雪花从页面中删除。
代码
function snow() {// 1、定义一片雪花模板var flake = document.createElement("div");// 雪花字符 ❄❉❅❆✻✼❇❈❊✥✺flake.innerHTML = "❆";flake.style.cssText = "position:absolute;color:#fff;";//获取页面的高度 相当于雪花下落结束时Y轴的位置var documentHieght = window.innerHeight;//获取页面的宽度,利用这个数来算出,雪花开始时left的值var documentWidth = window.innerWidth;//定义生成一片雪花的毫秒数var millisec = 100;//2、设置第一个定时器,周期性定时器,每隔一段时间(millisec)生成一片雪花;setInterval(function () {//页面加载之后,定时器就开始工作//随机生成雪花下落 开始 时left的值,相当于开始时X轴的位置var startLeft = Math.random() * documentWidth;//随机生成雪花下落 结束 时left的值,相当于结束时X轴的位置var endLeft = Math.random() * documentWidth;//随机生成雪花大小var flakeSize = 5 + 20 * Math.random();//随机生成雪花下落持续时间var durationTime = 4000 + 7000 * Math.random();//随机生成雪花下落 开始 时的透明度var startOpacity = 0.7 + 0.3 * Math.random();//随机生成雪花下落 结束 时的透明度var endOpacity = 0.2 + 0.2 * Math.random();//克隆一个雪花模板var cloneFlake = flake.cloneNode(true);//第一次修改样式,定义克隆出来的雪花的样式cloneFlake.style.cssText += `left: ${startLeft}px;opacity: ${startOpacity};font-size:${flakeSize}px;top:-25px;transition:${durationTime}ms;`;//拼接到页面中document.body.appendChild(cloneFlake);//设置第二个定时器,一次性定时器,//当第一个定时器生成雪花,并在页面上渲染出来后,修改雪花的样式,让雪花动起来;setTimeout(function () {//第二次修改样式cloneFlake.style.cssText += `left: ${endLeft}px;top:${documentHieght}px;opacity:${endOpacity};`;//4、设置第三个定时器,当雪花落下后,删除雪花。setTimeout(function () {cloneFlake.remove();}, durationTime);}, 0);}, millisec);
}
调用函数
snow();
2.3、设置背景粒子
分析
使用MorphSVGPlugin插件来将指定的多边形转换为路径。
首先,调用MorphSVGPlugin.convertToPath("polygon")
方法,将ID为“polygon”的多边形元素转换为路径元素。
然后,定义了两个命名空间常量,xmlns
和xlinkns
,分别用于设置XML命名空间和Xlink命名空间。
接下来,定义了两个辅助函数select
和selectAll
,用于选择一个或多个元素。
然后,通过select
函数选择了一些DOM元素,例如.pContainer
、.mainSVG
、#star
、.sparkle
、#tree
等。
接下来,定义了一些变量,showParticle
为true
表示显示粒子,particleColorArray
是一个颜色数组,particleTypeArray
是一个粒子类型数组,用于指定粒子的形状。
然后,定义了一个粒子池数组particlePool
、一个粒子计数器particleCount
以及一个粒子数量numParticles
。
代码
MorphSVGPlugin.convertToPath("polygon");var xmlns = "http://www.w3.org/2000/svg",xlinkns = "http://www.w3.org/1999/xlink",select = function (s) {return document.querySelector(s);},selectAll = function (s) {return document.querySelectorAll(s);},pContainer = select(".pContainer"),mainSVG = select(".mainSVG"),star = select("#star"),sparkle = select(".sparkle"),tree = select("#tree"),showParticle = true,particleColorArray = ["#E8F6F8","#ACE8F8","#F6FBFE","#A2CBDC","#B74551","#5DBA72","#910B28","#910B28","#446D39",],particleTypeArray = ["#star", "#circ", "#cross", "#heart"],// particleTypeArray = ['#star'],particlePool = [],particleCount = 0,numParticles = 201;
2.4、操作动画效果
分析
使用GSAP动画库(GreenSock Animation Platform)来操作SVG元素的动画效果。
首先,通过gsap.set
设置了SVG的可见性为“visible”,表示将SVG元素设置为可见状态。
接下来,使用gsap.set
设置了sparkle
元素的transformOrigin
为"50% 50%",并将其y坐标设置为-100,这将影响后续的动画效果。
然后定义了一个函数getSVGPoints
,用于获取SVG路径中的点坐标。该函数接受一个路径元素作为参数,通过MotionPathPlugin.getRawPath
方法获取路径的原始数据,并将其转换为点坐标的数组。
接着使用getSVGPoints
函数分别获取了.treePath
和.treeBottomPath
的点坐标,并存储在treePath
和treeBottomPath
变量中。
最后,创建了一个mainTl
的GSAP时间轴和一个starTl
的动画时间轴,用于控制整体的动画效果。
代码
// gsap动画库
gsap.set("svg", {visibility: "visible",
});gsap.set(sparkle, {transformOrigin: "50% 50%",y: -100,
});let getSVGPoints = (path) => {let arr = [];var rawPath = MotionPathPlugin.getRawPath(path)[0];rawPath.forEach((el, value) => {let obj = {};obj.x = rawPath[value * 2];obj.y = rawPath[value * 2 + 1];if (value % 2) {arr.push(obj);}//console.log(value)});return arr;
};
let treePath = getSVGPoints(".treePath");var treeBottomPath = getSVGPoints(".treeBottomPath");//console.log(starPath.length)
var mainTl = gsap.timeline({ delay: 0, repeat: 0 }),starTl;
2.5、定义闪烁效果
分析
定义一个名为flicker
的函数,用于实现一个闪烁效果。
该函数接受一个参数p
,表示要应用闪烁效果的对象。
首先,通过gsap.killTweensOf
方法停止对象p
的所有关于透明度的动画效果。
然后,使用gsap.fromTo
方法设置对象p
的透明度动画效果。
初始状态下,设置透明度为1,即完全可见。
然后,设置动画的持续时间为0.07秒,并将透明度设置为一个随机值,范围在0到1之间。
最后,设置动画重复播放,使得闪烁效果无限循环。
代码
function flicker(p) {//console.log("flivker")gsap.killTweensOf(p, { opacity: true });gsap.fromTo(p,{opacity: 1,},{duration: 0.07,opacity: Math.random(),repeat: -1,});}
2.6、定义粒子对象
分析
定义了一个名为createParticles
的函数,用于创建和初始化粒子(particles)。
首先,注释掉了变量step
的初始化代码和打印starPath.length
的语句。
接着,定义了变量i
,初始值为numParticles
。变量p
表示粒子的元素,particleTl
是粒子的动画时间轴,step
用于确定粒子在treePath
路径中的位置,pos
用于存储具体的粒子位置。
然后,使用while
循环逐个创建粒子对象。在每次循环中,通过select
方法选择一个粒子的类型,使用cloneNode(true)
方法对其进行克隆,并将克隆的粒子对象添加到mainSVG
中。
接下来,设置粒子的填充颜色,通过particleColorArray
数组来循环设置粒子的颜色属性。
然后,为粒子设置class
属性为particle
,并将其添加到particlePool
数组中,以便后续使用。
最后,使用gsap.set
方法将粒子对象的初始位置设置为(-100, -100),并设置transformOrigin
为"50% 50%"。
代码
function createParticles() {//var step = numParticles/starPath.length;//console.log(starPath.length)var i = numParticles,p,particleTl,step = numParticles / treePath.length,pos;while (--i > -1) {p = select(particleTypeArray[i % particleTypeArray.length]).cloneNode(true);mainSVG.appendChild(p);p.setAttribute("fill",particleColorArray[i % particleColorArray.length]);p.setAttribute("class", "particle");particlePool.push(p);//hide them initiallygsap.set(p, {x: -100,y: -100,transformOrigin: "50% 50%",});}
}
2.7、粒子对象播放
分析
定义一个名为getScale
的函数,用于生成一个随机的缩放比例。该函数使用了gsap.utils.random
方法,传入了最小值0.5、最大值3、步长0.001和布尔值参数true。这意味着生成的随机数将在0.5到3之间,并以0.001为步长。
接下来,定义了一个名为playParticle
的函数,用于播放粒子动画。在函数内部,首先判断是否允许显示粒子,如果不允许,则直接返回。
然后,通过particlePool
数组获取当前要播放的粒子对象。
接下来,使用gsap.set
方法设置粒子的初始位置为.pContainer
元素的x和y属性的值,并设置缩放比例为通过getScale
函数生成的随机数。
然后,创建一个时间轴对象tl
,并使用tl.to
方法对粒子对象进行动画操作。
在动画过程中,设置了动画的持续时间为0.61到6秒之间的随机数。
使用physics2D
属性实现了一个物理引擎效果,具体的速度、角度和重力值通过gsap.utils.random
方法生成。
同时,设置了粒子的缩放比例和旋转角度为随机的范围值。
设置了动画的缓动函数为power1
。
在动画开始时调用了flicker
函数,并传入了粒子对象作为参数。
在动画重复时,通过onRepeat
方法设置了粒子的缩放比例为通过getScale
函数生成的随机数。
代码
var getScale = gsap.utils.random(0.5, 3, 0.001, true); // 圣诞树开始绘画时小光点动画的特效(参数:最小值,最大值,延迟)function playParticle(p) {if (!showParticle) {return;}var p = particlePool[particleCount];gsap.set(p, {x: gsap.getProperty(".pContainer", "x"),y: gsap.getProperty(".pContainer", "y"),scale: getScale(),});var tl = gsap.timeline();tl.to(p, {duration: gsap.utils.random(0.61, 6),physics2D: {velocity: gsap.utils.random(-23, 23),angle: gsap.utils.random(-180, 180),gravity: gsap.utils.random(-6, 50),},scale: 0,rotation: gsap.utils.random(-123, 360),ease: "power1",onStart: flicker,onStartParams: [p],//repeat:-1,onRepeat: (p) => {gsap.set(p, {scale: getScale(),});},onRepeatParams: [p],});////particlePool[particleCount].play();particleCount++;//mainTl.add(tl, i / 1.3)particleCount = particleCount >= numParticles ? 0 : particleCount;
}
2.8、绘制星星
分析
定义一个名为drawStar
的函数,用于绘制星星动画。在函数内部,首先创建了一个时间轴对象starTl
,并设置了更新回调函数为playParticle
。
接下来,使用to
方法对.pContainer
和.sparkle
元素进行动画操作。在动画过程中,设置了动画的持续时间为6秒,并使用motionPath
属性指定了动画的路径为.treePath
元素,并禁用了自动旋转效果。设置了动画的缓动函数为linear
。
接着,使用to
方法对.pContainer
和.sparkle
元素进行动画操作。在动画开始时,通过onStart
函数将showParticle
变量设置为false
,表示不显示粒子。设置了动画的持续时间为1秒,并将元素的x和y属性设置为treeBottomPath[0].x
和treeBottomPath[0].y
。
然后,使用to
方法对.pContainer
和.sparkle
元素进行动画操作。在动画开始时,通过onStart
函数将showParticle
变量设置为true
,表示显示粒子。设置了动画的持续时间为2秒,并使用motionPath
属性指定了动画的路径为.treeBottomPath
元素,并禁用了自动旋转效果。设置了动画的缓动函数为linear
。
最后,使用from
方法对.treeBottomMask
元素进行动画操作。设置了动画的持续时间为2秒,并使用drawSVG
属性指定了路径的起始结束百分比为0% 0%
,即不显示路径,同时设置了画笔的颜色为#FFF
,表示白色。设置了动画的缓动函数为linear
。
代码
function drawStar() {starTl = gsap.timeline({ onUpdate: playParticle });starTl.to(".pContainer, .sparkle", {duration: 6,motionPath: {path: ".treePath",autoRotate: false,},ease: "linear",}).to(".pContainer, .sparkle", {duration: 1,onStart: function () {showParticle = false;},x: treeBottomPath[0].x,y: treeBottomPath[0].y,}).to(".pContainer, .sparkle",{duration: 2,onStart: function () {showParticle = true;},motionPath: {path: ".treeBottomPath",autoRotate: false,},ease: "linear",},"-=0")// 圣诞树中间那条横线动画 .treeBottomMask 是绑定class='treeBottomMask'这个标签.from(".treeBottomMask",{duration: 2,drawSVG: "0% 0%",stroke: "#FFF",ease: "linear",},"-=2");//gsap.staggerTo(particlePool, 2, {})
}
2.9、绘制圣诞树
分析
定义了一个名为drawMain
的函数,用于绘制主要的圣诞树动画。在函数内部,首先创建了一个时间轴对象mainTl
。
接下来,使用from
方法对.treePathMask
和.treePotMask
元素进行动画操作。设置了动画的持续时间为6秒,并使用drawSVG
属性指定了路径的起始结束百分比为0% 0%
,即不显示路径,同时设置了画笔的颜色为#FFF
表示白色。设置了动画的缓动函数为linear
。
然后,使用from
方法对.treeStar
元素进行动画操作。设置了动画的持续时间为3秒,并设置了元素的scaleY
属性为0、scaleX
属性为0.15,表示缩放比例,使用transformOrigin
属性指定了缩放的基点在元素的中心。设置了动画的缓动函数为elastic(1,0.5)
。
接着,使用to
方法对.sparkle
元素进行动画操作。设置了动画的持续时间为3秒,并将元素的透明度设置为0,使用了rough
缓动函数,该缓动函数会在动画期间以随机方式改变元素的透明度值。该动画将在前一个动画的结束点之后开始。
最后,使用to
方法对.treeStarOutline
元素进行动画操作。设置了动画的持续时间为1秒,并将元素的透明度设置为0.3,使用了rough
缓动函数,该缓动函数会在动画期间以随机方式改变元素的透明度值。该动画将在前一个动画的结束点之后开始。
代码
function drawMain() {mainTl// 圣诞树上半身轮廓动画.from([".treePathMask", ".treePotMask"], {duration: 6,drawSVG: "0% 0%",stroke: "#FFF",stagger: {each: 6,},duration: gsap.utils.wrap([6, 1, 2]),ease: "linear",})// 圣诞树头上的星星动画.from(".treeStar",{duration: 3,//skewY:270,scaleY: 0,scaleX: 0.15,transformOrigin: "50% 50%",ease: "elastic(1,0.5)",},"-=4")// 当绘画圣诞树的小光点绘制完时,让小光点消失.to(".sparkle",{duration: 3,opacity: 0,ease: "rough({strength: 2, points: 100, template: linear, taper: both, randomize: true, clamp: false})",},"-=0")// 给圣诞树头上的星星加个白色特效.to(".treeStarOutline",{duration: 1,opacity: 0.3,ease: "rough({strength: 2, points: 16, template: linear, taper: none, randomize: true, clamp: false})",},"+=1");/* .to('.whole', {
opacity: 0
}, '+=2') */
}
2.10、绘制星星背景动画
分析
定义一个名为drawStars
的函数,用于绘制星星背景动画。在函数内部,首先获取了stars
元素,然后创建了一个canvas
元素并将其上下文赋给ctx
变量。接着,设置了canvas
的宽度和高度为窗口的宽度和高度,并分别赋给w
和h
变量。
然后,定义了一些变量,包括色调色彩hue
、保存星星对象的数组stars
、计数器count
和最大星星数量maxStars
。
接下来,创建了一个用于绘制星星的源图像canvas2
,并获取其上下文赋给ctx2
变量。设置了canvas2
的宽度和高度为100,并创建了径向渐变gradient2
,以半径为0的圆开始,到半径为half
的圆结束。根据色调色彩hue
的不同,设置了不同的颜色。然后,使用路径arc
创建了一个圆,并使用fill
方法填充了渐变。
接着,定义了一个random
函数,用于生成指定范围内的随机数。
定义了一个maxOrbit
函数,用于根据屏幕宽高计算星星的移动范围。
创建了一个Star
构造函数,用于实例化星星对象。在构造函数中,设置了星星的移动半径、大小、圆心位置、初始角度、移动速度和透明度等属性,并将新创建的星星对象存入stars
数组。
Star
对象定义了一个draw
方法,用于绘制星星。在该方法中,根据星星的移动半径和角度计算出星星在屏幕上的位置。根据随机数判断星星是否变亮或变暗,并根据透明度绘制星星。最后,更新星星的角度。
然后,使用循环创建了指定数量的星星对象。
最后,定义了一个animation
函数,用于执行星星的动画效果。在该函数中,首先绘制了一个半透明的背景颜色,然后使用for
循环调用每个星星对象的draw
方法进行绘制。最后,通过window.requestAnimationFrame
方法不断循环调用animation
函数,实现动画效果。
代码
function drawStars() {let canvas = document.getElementById("stars"),ctx = canvas.getContext("2d"),w = (canvas.width = window.innerWidth),h = (canvas.height = window.innerHeight),hue = 37, //色调色彩stars = [], //保存所有星星count = 0, //用于计算星星maxStars = 1300; //星星数量//canvas2是用来创建星星的源图像,即母版,//根据星星自身属性的大小来设置var canvas2 = document.createElement("canvas"),ctx2 = canvas2.getContext("2d");canvas2.width = 100;canvas2.height = 100;//创建径向渐变,从坐标(half,half)半径为0的圆开始,//到坐标为(half,half)半径为half的圆结束var half = canvas2.width / 2,gradient2 = ctx2.createRadialGradient(half, half, 0, half, half, half);gradient2.addColorStop(0.025, "#CCC");//hsl是另一种颜色的表示方式,//h->hue,代表色调色彩,0为red,120为green,240为blue//s->saturation,代表饱和度,0%-100%//l->lightness,代表亮度,0%为black,100%位whitegradient2.addColorStop(0.1, "hsl(" + hue + ", 61%, 10%)");gradient2.addColorStop(0.25, "hsl(" + hue + ", 64%, 2%)");gradient2.addColorStop(1, "transparent");ctx2.fillStyle = gradient2;ctx2.beginPath();ctx2.arc(half, half, half, 0, Math.PI * 2);ctx2.fill();// End cachefunction random(min, max) {if (arguments.length < 2) {max = min;min = 0;}if (min > max) {var hold = max;max = min;min = hold;}//返回min和max之间的一个随机值return Math.floor(Math.random() * (max - min + 1)) + min;}function maxOrbit(x, y) {var max = Math.max(x, y),diameter = Math.round(Math.sqrt(max * max + max * max));//星星移动范围,值越大范围越小,return diameter / 2;}var Star = function () {//星星移动的半径this.orbitRadius = random(maxOrbit(w, h));//星星大小,半径越小,星星也越小,即外面的星星会比较大this.radius = random(60, this.orbitRadius) / 8;//所有星星都是以屏幕的中心为圆心this.orbitX = w / 2;this.orbitY = h / 2;//星星在旋转圆圈位置的角度,每次增加speed值的角度//利用正弦余弦算出真正的x、y位置this.timePassed = random(0, maxStars);//星星移动速度this.speed = random(this.orbitRadius) / 50000;//星星图像的透明度this.alpha = random(2, 10) / 10;count++;stars[count] = this;};Star.prototype.draw = function () {//星星围绕在以屏幕中心为圆心,半径为orbitRadius的圆旋转var x = Math.sin(this.timePassed) * this.orbitRadius + this.orbitX,y = Math.cos(this.timePassed) * this.orbitRadius + this.orbitY,twinkle = random(10);//星星每次移动会有1/10的几率变亮或者变暗if (twinkle === 1 && this.alpha > 0) {//透明度降低,变暗this.alpha -= 0.05;} else if (twinkle === 2 && this.alpha < 1) {//透明度升高,变亮this.alpha += 0.05;}ctx.globalAlpha = this.alpha;//使用canvas2作为源图像来创建星星,//位置在x - this.radius / 2, y - this.radius / 2//大小为 this.radiusctx.drawImage(canvas2,x - this.radius / 2,y - this.radius / 2,this.radius,this.radius);//没旋转一次,角度就会增加this.timePassed += this.speed;};//初始化所有星星for (var i = 0; i < maxStars; i++) {new Star();}function animation() {//以新图像覆盖已有图像的方式进行绘制背景颜色ctx.globalCompositeOperation = "source-over";ctx.globalAlpha = 0.9; //尾巴ctx.fillStyle = "hsla(" + hue + ", 64%, 6%, 2)";ctx.fillRect(0, 0, w, h);//源图像和目标图像同时显示,重叠部分叠加颜色效果ctx.globalCompositeOperation = "lighter";for (var i = 1, l = stars.length; i < l; i++) {stars[i].draw();}//调用该方法执行动画,并且在重绘的时候调用指定的函数来更新动画//回调的次数通常是每秒60次window.requestAnimationFrame(animation);}animation();
}
2.11、定义初始化函数并调用
分析
执行了init
函数,接着依次执行了createParticles
、drawStar
、drawMain
和drawStars
等函数,并将它们添加到了mainTl
时间轴中。最后,通过gsap.globalTimeline.timeScale(1.5)
方法设置了全局时间轴的绘画速率。
代码
function init() {let element = document.getElementById("header");for(let i = 0; i < myLabels.length; i++) {let _p = document.createElement("p");_p.className = "header-item";_p.innerHTML = myLabels[i];element.appendChild(_p);}let labels = document.getElementsByClassName('header-item');for(let i = 0; i < myLabels.length; i++) {setTimeout(() => {labels[i].classList.add("show");}, 1000 * i);}
}
init();
createParticles()
drawStar();
drawMain();
mainTl.add(starTl, 0);
gsap.globalTimeline.timeScale(1.5); // 圣诞树开始绘画时小光点动画的绘画速率,越大越快
drawStars();
3、结尾
大部分都是js代码,其实还有一些html和css需要自己研究一下,需要源码私信,祝大家平安夜圣诞夜快乐