极简旋转loading动画编辑器

问题分类


loading 动画的可编辑

 

问题描述


电子课本之前的书本加载 loading 不满足章节之间的跳转 loading动画的要求,需要制作新的 loading 动画

 

 

 

原因分析


 

 

 

 

 

解决方案


制作一个 loading 动画并能编辑其最终效果:

极简旋转loading动画编辑器

下面是最终实现调用的代码:

    const _obj = {     interval: "0.15s",     bgColor: "#1bf",     size: "20px",     shrink: 3.75,     };     const config = mkResponsive(_obj);       // 至此实现了数据到视图层的绑定     //   setTimeout(() => {     //     config.bgColor = "#f00";     //     config.size = "50px";     //     config.shrink = 5;     //     config.interval = "0.2s";     //   }, 2000);       // 至此实现编辑器视图到数据的绑定     editor.append(config,"interval","","速度:","text");     editor.append(config,"shrink","","伸缩度:","range",1,10);     editor.append(config,"size","px","点直径:","range",10,100);     editor.append(config,"bgColor","","点颜色:","color");

 

大体分两步,第一步实现数据到动画的绑定,这里特意做了多个属性同步更改只实现一次重刷的效果,类似 React 使用 SetState 收集帧前状态,但是因为这里使用类似 Vue3 的 Proxy 对象,因此可以直接通过点操作符实现,当然具体实现要简单很多。

第二步是编辑器到数据的绑定,也就是 css3 自带 input 类元素的变动会反射到数据上。

这样,最终效果就是:编辑器 => 数据 => 动画

 

首先实现 css 动画,一个 div 包含四个小球,此 div 旋转 45 度后得到另一个 div:

极简旋转loading动画编辑器

每个小圆给到一个不断循环 scale 的 animation 播放,8 个小球依次具备 interval 时长的播放时差,因为单个 html 文件,直接用 css 变量:

      .dots2 > li:nth-child(1) {         animation-delay: calc(var(--interval) * -7);       }       .dots1 > li:nth-child(2) {         animation-delay: calc(var(--interval) * -6);       }       .dots2 > li:nth-child(2) {         animation-delay: calc(var(--interval) * -5);       }       .dots1 > li:nth-child(3) {         animation-delay: calc(var(--interval) * -4);       }       .dots2 > li:nth-child(3) {         animation-delay: calc(var(--interval) * -3);       }       .dots1 > li:nth-child(4) {         animation-delay: calc(var(--interval) * -2);       }       .dots2 > li:nth-child(4) {         animation-delay: calc(var(--interval) * -1);       }

然后是 css 中定义的动画相关的变量,用来方便 js 统一修改:

      :root {         --interval: 0.2s;         --shrink: 3.75;         --bgColor: #1bf;         --size: 20px;       }

 

接下是数据响应式的实现:

      // 响应式处理器生成工厂       const responsiveHandlerFactory = () => {         const setPropHandlers = [];         const setPropHandlerCtxMap = new Map();                  const addSetPropHandler = (f) => {           setPropHandlers.push(f);           setPropHandlerCtxMap.set(f, Object.create(null));         };         const delSetPropHandler = (f) => {           const index = setPropHandlers.indexOf(f);           if (index !== -1) {             setPropHandlers.splice(index, 1);             setPropHandlerCtxMap.delete(f);           }         };         const responsiveHandler = {           set(obj, prop, val) {             for (let f of setPropHandlers) {               f(setPropHandlerCtxMap.get(f), obj, prop, val);             }             obj[prop] = val;             return true;           },         };           return {           responsiveHandler,           addSetPropHandler,           delSetPropHandler,         };       };

这里是一个极其简单粗糙的实现,给每个注册进来的 handler 创建一个独立的上下文,且使用 Map 记录。

这样做的原因是为了实现每个代理对象属性修改后的延迟响应。

这样使用 addSetPropHandler 就可以注册对象属性变更后的 UI 渲染逻辑了。

 

最后再实现一个极简编辑器:

      // 编辑器       const editor = {         edbox: null,         init() {           this.edbox = document.createElement("div");           const style = {             position: "fixed",             left: "20px",             top: "20px",             width: "300px",             height: "200px",             border: "1px solid #ccc",             borderRadius: "5px",             padding: "10px"           };           ((obj) => {             for (let key in obj) {               this.edbox.style[key] = obj[key];             }           })(style);           document.body.appendChild(this.edbox);         },         handlers: {},         append(obj, prop, unit, showText, type, min, max) {           let str = "";           // 生成随机函数名           const fname = "f" + Math.random().toString().slice(-5);           this.handlers[fname] = (val) => {             obj[prop] = val + unit;           };           str = `<div><span>${showText}</span><input type="${type}" ${             type === "range" && ` min=${min || 0} max=${max || 1}`           } value="${obj[prop]}" on${type === "range" ? "change" : "blur"}="editor.handlers.${fname}(event.target.value)" /></div>`;           this.edbox.innerHTML += str;         },       };

 

 

 

纠正措施


 

 

 

案例推广


本案例可推广到XXX项目(XXX项目也存在本案例的问题)

 

技术支持


对本案例的解决方案如有疑问或改进建议,请与作者联系。

上一篇:数据缺失类型:MCAR、MAR、MNAR


下一篇:[Python] 一文搞定合并区间及定会议室问题