js实现夜晚月亮下云层上流星雨动画效果代码
代码语言:html
所属分类:动画
代码描述:js实现夜晚月亮下云层上流星雨动画效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> /* ------------reset------------ */ * { margin: 0; padding: 0; } html, body { width: 100%; min-width: 1000px; height: 100%; min-height: 400px; overflow: hidden; } .container { position: relative; width: 100%; height: 100%; min-width: 100px; min-height: 400px; } /* ------------画布 ------------ */ /* 遮罩层 */ #mask { position: absolute; width: 100%; height: 100%; background: rgba(0, 0, 0, .8); z-index: 900; } /* 天空背景 */ #sky { width: 100%; height: 100%; background: linear-gradient(rgba(0, 150, 255, 1), rgba(0, 150, 255, .8), rgba(0, 150, 255, .5)); } /* 月亮 */ #moon { position: absolute; top: 50px; right: 200px; width: 120px; height: 120px; background: rgba(251, 255, 25, 0.938); border-radius: 50%; box-shadow: 0 0 20px rgba(251, 255, 25, 0.5); z-index: 9999; } /* 闪烁星星 */ .blink { position: absolute; background: rgb(255, 255, 255); border-radius: 50%; box-shadow: 0 0 5px rgb(255, 255, 255); opacity: 0; z-index: 10000; animation: blingbling 2s linear infinite; } /* 流星 */ .star { position: absolute; opacity: 0; animation: fall 5s linear infinite; z-index: 10000; } .star::after { content: ""; display: block; border: solid; border-width: 2px 0 2px 80px; /*流星随长度逐渐缩小*/ border-color: transparent transparent transparent rgba(255, 255, 255, 1); border-radius: 2px 0 0 2px; transform: rotate(-45deg); transform-origin: 0 0 0; box-shadow: 0 0 20px rgba(255, 255, 255, .3); } /* 云 */ .cloud { position: absolute; width: 100%; height: 100px; /* background: #fff; */ animation: float 2s linear infinite; } .cloud-1 { bottom: -100px; z-index: 1000; opacity: 1; animation-delay: 1s; transform: scale(1.5); -webkit-transform: scale(1.5); -moz-transform: scale(1.5); -ms-transform: scale(1.5); -o-transform: scale(1.5); } .cloud-2 { left: -100px; bottom: -50px; z-index: 999; opacity: .5; transform: rotate(7deg); animation-delay: 1.4s; } .cloud-3 { left: 120px; bottom: -50px; z-index: 999; opacity: .1; transform: rotate(-10deg); animation-delay: 1.8s; } .circle { position: absolute; border-radius: 50%; background: #fff; } .circle-1 { width: 100px; height: 100px; top: -50px; left: 10px; } .circle-2 { width: 150px; height: 150px; top: -50px; left: 30px; } .circle-3 { width: 300px; height: 300px; top: -100px; left: 80px; } .circle-4 { width: 200px; height: 200px; top: -60px; left: 300px; } .circle-5 { width: 80px; height: 80px; top: -30px; left: 450px; } .circle-6 { width: 200px; height: 200px; top: -50px; left: 500px; } .circle-7 { width: 100px; height: 100px; top: -10px; left: 650px; } .circle-8 { width: 50px; height: 50px; top: 30px; left: 730px; } .circle-9 { width: 100px; height: 100px; top: 30px; left: 750px; } .circle-10 { width: 150px; height: 150px; top: 10px; left: 800px; } .circle-11 { width: 150px; height: 150px; top: -30px; left: 850px; } .circle-12 { width: 250px; height: 250px; top: -50px; left: 900px; } .circle-13 { width: 200px; height: 200px; top: -40px; left: 1000px; } .circle-14 { width: 300px; height: 300px; top: -70px; left: 1100px; } /*动画*/ /*云*/ /*@keyframes float { 0% { bottom: -50px; } 50% { bottom: -45px; } 100% { bottom: -50px; } }*/ </style> </head> <body> <!-- 相比于纯CSS写法,动画的效果更华丽,可以一直在随机位置变更节点,动画看起来更具有“活性”。但同时对于性能的消耗也更高 另外,由于动画函数采用requestAnimationFrame封装,然后通过setInterval不停添加节点并调用动画 如果跳转至其他页面,在此期间定时器会不停添加节点,而动画则一直处于暂停状态 再次返回页面时,动画效果会聚集,然后一瞬间释放出来,可能浏览器会崩溃 --> <div class="container"> <div id="mask"></div> <div id="sky"></div> <div id="moon"></div> <div id="stars"></div> <div class="cloud cloud-1"></div> <div class="cloud cloud-2"></div> <div class="cloud cloud-3"></div> </div> <script> //流星动画 setInterval(function () { const obj = addChild("#sky", "div", 2, "star"); for (let i = 0; i < obj.children.length; i++) { const top = -50 + Math.random() * 200 + "px", left = 200 + Math.random() * 1200 + "px", scale = 0.3 + Math.random() * 0.5; const timer = 1000 + Math.random() * 1000; obj.children[i].style.top = top; obj.children[i].style.left = left; obj.children[i].style.transform = `scale(${scale})`; requestAnimation({ ele: obj.children[i], attr: ["top", "left", "opacity"], value: [150, -150, .8], time: timer, flag: false, fn: function () { requestAnimation({ ele: obj.children[i], attr: ["top", "left", "opacity"], value: [150, -150, 0], time: timer, flag: false, fn: () => { obj.parent.removeChild(obj.children[i]); } }); } }); } }, 1000); //闪烁星星动画 setInterval(function () { const obj = addChild("#stars", "div", 2, "blink"); for (let i = 0; i < obj.children.length; i++) { const top = -50 + Math.random() * 500 + "px", left = 200 + Math.random() * 1200 + "px", round = 1 + Math.random() * 2 + "px"; const timer = 1000 + Math.random() * 4000; obj.children[i].style.top = top; obj.children[i].style.left = left; obj.children[i].style.width = round; obj.children[i].style.height = round; requestAnimation({ ele: obj.children[i], attr: "opacity", value: .5, time: timer, flag: false, fn: function () { requestAnimation({ ele: obj.children[i], attr: "opacity", value: 0, time: timer, flag: false, fn: function () { obj.parent.removeChild(obj.children[i]); } }); } }); } }, 1000); //月亮移动 requestAnimation({ ele: "#moon", attr: "right", value: 1200, time: 10000000 }); // 添加云 const clouds = addChild(".cloud", "div", 14, "circle", true); for (let i = 0; i < clouds.children.length; i++) { for (let j = 0; j < clouds.children[i].length;) { clouds.children[i][j].classList.add(`circle-${++j}`); } } //云动画 let flag = 1; setInterval( function () { const clouds = document.querySelectorAll(".cloud"); const left = Math.random() * 5; bottom = Math.random() * 5; let timer = 0; for (let i = 0; i < clouds.length; i++) { requestAnimation({ ele: clouds[i], attr: ["left", "bottom"], value: flag % 2 ? [-left, -bottom] : [left, bottom], time: timer += 500, flag: false, fn: function () { requestAnimation({ ele: clouds[i], attr: ["left", "bottom"], value: flag % 2 ? [left, bottom] : [-left, -bottom], time: timer, flag: false }); } }); } flag++; }, 2000); //———————————————————————————————————————————动画——————————————————————————————————————————————————— //运动动画,调用Tween.js //ele: dom | class | id | tag 节点 | 类名 | id名 | 标签名,只支持选择一个节点,class类名以及标签名只能选择页面中第一个 //attr: attribute 属性名 //value: target value 目标值 //time: duration 持续时间 //tween: timing function 函数方程 //flag: Boolean 判断是按值移动还是按位置移动,默认按位置移动 //fn: callback 回调函数 //增加返回值: 将内部参数对象返回,可以通过设置返回对象的属性stop为true打断动画 function requestAnimation(obj) { //—————————————————————————————————————参数设置————————————————————————————————————————————— //默认属性 const parameter = { ele: null, attr: null, value: null, time: 1000, tween: "linear", flag: true, stop: false, fn: "" }; //合并传入属性 Object.assign(parameter, obj); //覆盖重名属性 //—————————————————————————————————————动画设置————————————————————————————————————————————— //创建运动方程初始参数,方便复用 let start = 0; //用于保存初始时间戳 let target = typeof parameter.ele === "string" ? document.querySelector(parameter.ele) : parameter.ele, //目标节点 attr = parameter.attr, //目标属性 beginAttr = parseFloat(getComputedStyle(target)[attr]), //attr起始值 value = parameter.value, //运动目标值 count = value - beginAttr, //实际运动值 time = parameter.time, //运动持续时间, tween = parameter.tween, //运动函数 flag = parameter.flag, callback = parameter.fn, //回调函数 curVal = 0; //运动当前值 //判断传入函数是否为数组,多段运动 (function () { if (attr instanceof Array) { beginAttr = []; count = []; for (let i of attr) { const val = parseFloat(getComputedStyle(target)[i]); beginAttr.push(val); count.push(value - val); } } if (value instanceof Array) { for (let i in value) { count[i] = value[i] - beginAttr[i]; } } })(); //运动函数 function animate(timestamp) { if (parameter.stop) return; //打断 //存储初始时间戳 if (!start) start = timestamp; //已运动时间 let t = timestamp - start; //判断多段运动 if (beginAttr instanceof Array) { // const len = beginAttr.length //存数组长度,复用 //多段运动第1类——多属性,同目标,同时间/不同时间 if (typeof count === "number") {//同目标 //同时间 if (typeof time === "number") { if (t > time) t = time; //判断是否超出目标值 //循环attr,分别赋值 for (let i in beginAttr) { if (flag) curVal = Tween[tween](t, beginAttr[i], count, time); //调用Tween,返回当前属性值,此时计算方法为移动到写入位置 else curVal = Tween[tween](t, beginAttr[i], count + beginAttr[i], time); //调用Tween,返回当前属性值,此时计算方法为移动了写入距离 if (attr[i] === "opacity") target.style[attr[i]] = curVal; //给属性赋值 else target.style[attr[i]] = curVal + "px"; //给属性赋值 if (t < time) requestAnimationFrame(animate); //判断是否运动完 else callback && callback(); //调用回调函数 } return; } //不同时间 if (time instanceof Array) { //循环time,attr,分别赋值 for (let i in beginAttr) { //错误判断 if (!time[i] && time[i] !== 0) { .........完整代码请登录后点击上方下载按钮下载查看
网友评论0