2019年是我变化最大的一年,不只仅是技术,沟通交流与能力等各方面更让我清晰的认识到了本身的不足之处,学习的路还有很长很长,我有必要写出一篇文章来总结本身的这一年,以怀念个人2019。javascript
我是3月份加入的这家公司(四川领梵数字科技有限公司),而后直到如今,参与公司的核心产品发布后台管理系统与发布系统小程序的开发以及维护,也包含测试工做。php
起初,我对公司的产品也是只知其一;不知其二,个人老大给我讲的时候,包括我接任的前端开发的那位大哥也给我讲过。但那个时候,我仍是一直处于懵懵懂懂的,隐约知道公司的产品是作展厅的,而我加入进来,主要也是参与发布系统外接一些外包项目。css
可是,我隐约知道一点,就是须要学习pixi.js
,这是一款编写2D精灵渲染的引擎。我进来的第一项工做就是熟悉已经写好的架构系统代码,随着开发的深刻,我对这个产品也渐渐熟悉了。html
3月到4月一个月的时间我主要就是熟悉发布后台管理系统的代码,以及维护一些和添加一些新功能,4月份到5月份,主要开发了发布系统的小程序的初版,因为没有设计,尽管基本要求的功能完成了,并且花的时间也很少,也就一个星期的时间,因此初版小程序就这么直接废弃了。前端
第二个星期,开发第二版小程序,这一版有了设计,通过一个月的时间开发和维护,基本功能也完成了,而且上线成功运营当中。vue
小程序的模块不算多,大体分为以下模块:java
这其中涉及到的基本功能也都包含到了,包括微信受权,支付,扫一扫,短信验证,数据的加密(后台作)与解密(前端作)。node
小程序所用到的技术:uni-app。jquery
图标是采用设计给的,布局是手写的样式,没有用到插件。webpack
学到了什么?:参考了iview的row与col组件以及model组件的源码,在此基础上也本身封装了row和col组件以及model组件运用到了小程序当中。详情代码展现以下:
util.js:
/* * 功能:经常使用函数 * 做者:eveningwater * 日期:2019/3/6 */ // 往上查找组件的父组件 export function findComponentUp(context,componentName,componentNames){ componentNames = typeof componentName === 'string' ? [componentName] : componentName; let parent = context.$parent; let name = parent.$options.name; //若是父组件不是传入的组件名,则循环往上查找,直到找到父组件为传入的组件名为止 while(parent && (!name || componentName.indexOf(name) === -1)){ parent = parent.$parent; if(parent)name = parent.$options.name; } return parent; } // 往下查找组件的子组件 export function findComponentDown(context,componentName){ const childrens = context.$children; let children = null; if(childrens.length){ // 循环遍历数组 // for(const child of childrens){ // const name = child.$options.name; // if(name === componentName){ // children = child; // break; // }else{ // children = findComponentDown(child,componentName); // if(children)break; // } // } for(let k = 0,len = childrens.length; k < len;k++){ const name = childrens[k].$options.name; if(name === componentName){ children = childrens[k]; break; }else{ children = findComponentDown(childrens[k],componentName); if(children)break; } } } return children } // 查找组件的全部父组件 export function findComponentsUp(context,componentName){ let parents = []; if(parent){ const name = parent.$options.name; if(name === componentName)parents.push(parent); return parents.concat(findComponentsUp(parent,componentName)); }else{ return []; } } // 查找组件的全部子组件 export function findComponentsDown(context, componentName) { let components = []; return context.$children.forEach((child) => { if (child.$options.name === componentName) components.push(child); let foundChild = findComponentsDown(child, componentName); return components.concat(foundChild); }) } // 查找组件的兄弟组件 export function findComponentsBrother(context, componentName, exceptSelf = true) { // 找到当前组件的父组件的全部子组件 let childComponents = context.$parent.$children.filter((item) => { return item.$options.name === componentName; }) // 全部子组件中包含自身组件的索引 let selfIndex = childComponents.findIndex((item) => { return context._uid === item._uid; }) // 是否删除自身组件 if (exceptSelf) childComponents.splice(selfIndex, 1); return childComponents; } 复制代码
row.vue:
<template> <div :style="gutterObject" class="ew-row"> <slot></slot> </div> </template> <script> import {findComponentDown,findComponentsBrother} from './util.js' export default { props:{ gutter:{ type:[String,Number], default:0 } }, data() { return { }; }, computed:{ // 间隔绑定 gutterObject(){ let gutter = parseInt(this.gutter),style = {}; if(gutter){ style = 'margin-left:'+ gutter / -2 + 'px;' + 'margin-right:' + gutter / -2 + 'px;'; } return style; } }, mounted(){ }, methods:{ updateGutter(gutter){ // 找到当前组件的col组件 let ewCol = findComponentDown(this,'ewCol'); let ewCols = findComponentsBrother(ewCol,'ewCol',false); if(ewCols.length){ ewCols.forEach((child) => { if(gutter){ child.gutter = gutter; } }) } } }, watch:{ 'gutter':{ handler(val){ if(val){ // 若是gutter不为0,向row组件下的col组件传递gutter值 this.updateGutter(val) } }, deep:true } } } </script> <style> @import './component.css'; </style> 复制代码
col.vue:
<template> <div :style="gutterObject" :class="classObject" class="ew-col"> <slot></slot> </div> </template> <script> import { findComponentUp } from './util.js' export default { props: { span: { type: [String, Number], default: 0 } }, data() { return { gutter:0 }; }, computed: { // 区块间隔 gutterObject(){ let gutter = parseInt(this.gutter); if(gutter){ return 'padding-left:' + gutter / 2 + 'px;' + 'padding-right:' + gutter / 2 + 'px;'; } }, // 栅格类绑定 classObject() { let span = parseInt(this.span); if (span) { return 'ew-col-span-' + span; } } }, methods:{ updateGutter(){ const ewRow = findComponentUp(this,'ewRow'); if(ewRow){ ewRow.updateGutter(ewRow.gutter); } } }, mounted() { this.updateGutter(); } } </script> <style> @import './component.css'; </style> 复制代码
model.vue:
<template> <view class="modal-mask" @click="$emit('on-ew-close',$event)" :class="className"> <view class="modal-content" @click="$emit('on-ew-open',$event)"> <text class="modal-title" v-if="message.title">{{ message.title }}</text> <text class="modal-content-text" v-if="message.content">{{ message.content }}</text> <slot name="content"></slot> <button type="primary" @click="$emit('on-ew-sure')" class="surebtn" v-if="!showCancel">肯定</button> <ew-row v-else> <ew-col span="12"> <view @click="$emit('on-ew-sure')" class="surebtn">肯定</view> </ew-col> <ew-col span="12"> <view @click="$emit('on-ew-cancel')" class="cancelbtn" >取消</view> </ew-col> </ew-row> </view> </view> </template> <script> export default { props:{ message:{ type:Object, default:() => { return {} } }, showCancel:{ type:Boolean, default:false }, className:String }, computed:{ }, data() { return { }; }, mounted(){ }, methods:{ } } </script> <style> @import './component.css'; </style> 复制代码
遇到的问题:在开发当中我遇到了uni-app框架还不是彻底支持vue插槽语法。我后面只能将不生效的组件重写一遍。
后面由于要熟悉老大写的引擎代码,因此我就花了一周时间将pixi.js基础学习了一下,并趁着不是太忙的时候,记录了下来,写成了本身的笔记。文档内容。
对于发布系统,我仍是挺自豪的,由于里面涉及到了不少功能都比较复杂,这其中包括组件事件编辑器
,动画时间轴
,素材库
,动画设置
等。
先来讲明一下发布系统吧,这个产品有些相似易企秀的产品,但与易企秀又有着区别。并且发布系统的主要特点就是能够编辑粒子动画。
目前一些基本的编辑模板须要用到的组件都开发完成了,也能制做出一些模板。如如下就是我本身使用发布系统所制做出的模板。
后期也能够针对需求来完成一些特点的功能,这也是与易企秀的区别所在。
制做模板所用的引擎就是使用pixi.js
编写的。发布系统的代码也是恐怖,就单单一个编辑模板里面所涉及到的代码就有二十多个文件,(PS:原本是没有这么多个文件的,所有代码集中在一个文件中,一个文件有几万行代码,我看着头疼,就一步步的将代码抽离出来,因此花的时间也比较多)。下面展现一下时间轴代码,其它就不展现了。以下图(只展现了其中一个文件夹的文件)所示:
时间轴代码:
timeline.vue:
<template> <div class="timeline" v-drag ref="timeline" :style="closeStyle"> <!--让时间轴拖拽的元素 --> <div class="timeline-drag-el"></div> <!-- 关闭时间轴 --> <el-tooltip effect="dark" content="关闭" placement="top-start"> <i :class="iconClassName" class="close-timeline" @click="closeTimeLine"></i> </el-tooltip> <div class="timeline-content-container" :style="closeStyle"> <!-- 组件管理 --> <div class="timeline-component-group"> <div class="timeline-component-header">组件名</div> <div class="timeline-component-name" v-for="(com,index) in componentArr" :key="index" :style ="curComponentIndex === index ? 'background-color:rgb(155, 199, 226);color:#fff;' : ''" @click="selectComponent(com,index)"> {{ com.styles.name }} <el-tooltip effect="dark" content="播放" placement="top-start"> <i class="el-icon-caret-right play-component-icon" @click="playComponentAnimation(com)"></i> </el-tooltip> <el-tooltip effect="dark" content="删除" placement="top-start"> <i class="el-icon-delete delete-component-icon" @click="deleteComponent(com)"></i> </el-tooltip> </div> </div> <div class="timeline-timeline-container" ref="timeLineScroll"> <!-- 模拟时间轴横向滚动条 --> <div class="timeline-scrollbar-wrapper"> <div class="timeline-scrollbar" style="width:1479px;"> <div class="timeline-track" style="width:1479px;"> <div class="timeline-thumb" style="width:990px;" :style="moveLeft" @mousedown="changeLeft"></div> </div> </div> </div> <div class="timeline-container"> <div class="timeline-content-overview" ref="timeLineView" :style="{ 'min-width':'100%',width:spotArr * 195 + 22 + 'px',left:-viewLeft + 'px'}"> <!-- 时间轴刻度线 --> <div class="timeline-content-iframe"> <div class="timeline-content-marker" v-for="(t,index) in spotArr" :key="index" :style="{ width:'195px'}"> <span class="timeline-text">{{ index + 's' }}</span> </div> </div> <!-- 时间管理 --> <div class="timeline-layer-container"> <div class="timeline-layer-area" v-for="(com,index) in componentArr" :key="index" :style ="curComponentIndex === index ? 'background-color:rgb(155, 199, 226);color:#fff;' : ''"> <div class="timeline-layer-animation" v-for = "(node,_index) in com['animation']['group'][0].ani" :key="_index" @mousedown="changeDelayOrDuration($event,node,_index)" :style="computedStyle(com['animation']['group'][0].ani,node,_index)"> <span class="timeline-layer-delay" v-show="node.delay * 1000 >= 150">{{ node.delay * 1000 }}</span> <span class="timeline-layer-duration">{{ node.duration * 1000 }}</span> <div class="timeline-layer-resize-handle-delay"></div> <div class="timeline-layer-resize-handle-duration"></div> </div> </div> </div> <!-- 时间轴游标 --> <div class="timeline-drag-handle" :style="{ left:spotLeft +'px'}" @mousedown="changeSpot"> <div class="timline-drag-spot"></div> </div> </div> </div> </div> </div> </div> </template> <script src="../js/_timeline.js"></script> <style lang="stylus"> @import '../css/timeline.styl' </style> 复制代码
_timeline.js:
export default { name: "timeline", props: ['timeLineData', 'componentIndex'], data() { return { left: 0,//模拟滚动条左偏移量 viewLeft: 0,//刻度线左偏移量 componentArr: [],//组件数组 spotLeft: -10,//拖拽左偏移量 curComponentIndex: this.componentIndex,//当前组件 closeStyle: {}, isCloseTimeLine: false, iconClassName:"el-icon-remove-outline" } }, computed: { // 模拟的滚动条的左偏移量 moveLeft() { return { left: this.left + 'px' } }, //刻度线数,也许是一个数组 spotArr() { return Math.ceil(this.maxLeftOrMaxTime('time')() / 1000) + 1 || 11; }, maxScrollLeft() { // 990为滚动轨道的宽度,4px减小误差 return this.$refs.timeLineScroll.offsetWidth - 994; }, viewMaxLeft() { // 一页显示6个刻度线,195为每一个刻度线宽度,剩余恰好就是this.spotArr - 6个,因此滚动最大为195 * (this.spotArr - 6) if (this.spotArr > 6) { return 195 * (this.spotArr - 6); } else { return 0; } } }, mounted() { // 时间轴数据 if (this.timeLineData) { this.componentArr = this.timeLineData; } }, methods: { closeTimeLine() { this.isCloseTimeLine = !this.isCloseTimeLine; if (this.isCloseTimeLine) { this.$set(this.closeStyle, 'width', 0); this.$set(this.closeStyle, 'height', 0); this.$set(this.closeStyle, 'padding', 0); this.iconClassName = 'el-icon-full-screen'; } else { this.closeStyle = {}; this.iconClassName = 'el-icon-remove-outline'; } }, // 计算宽度与左偏移量 computedStyle(nodeArr, node, index) { if (index <= 0) { return { width: 10 * (node.duration * 1000 / 50) + 'px', left: 10 * node.delay * 1000 / 50 + 21 + 'px' } } else { //初始左偏移量 let left = 21; nodeArr.forEach((n, nIndex) => { if (nIndex <= index) { left += 10 * (n.delay * 1000 / 50) } if (nIndex <= index - 1) { left += 10 * (n.duration * 1000 / 50); } }) return { width: 10 * (node.duration * 1000 / 50) + 'px', left: left + 'px' } } }, // 改变左偏移量 changeLeft() { document.onmousemove = (e) => { this.left = e.pageX > this.maxScrollLeft ? this.maxScrollLeft : e.pageX <= 0 ? 0 : e.pageX; this.viewLeft = e.pageX > this.viewMaxLeft ? this.viewMaxLeft : e.pageX <= 0 ? 0 : e.pageX; } this.cancelEvent(); }, // 改变延迟或者执行时间 changeDelayOrDuration(event, node, index) { // 判断拖拽方向 let direction = event.clientX; // 块的总宽 let total = 10 * (node.duration * 1000 / 50); // 若是拖拽的是执行时间轴,则改变执行时间,不然改变延迟时间 let isDuration = event.target.className.indexOf('duration') > - 1 ? true : false; document.onmousemove = (e) => { if (e.clientX >= direction) { if (isDuration) { node.duration = (node.duration * 1000 + 50) / 1000; } else { node.delay = (node.delay * 1000 + 50) / 1000; } } else { if (isDuration) { node.duration = (node.duration * 1000 - 50) / 1000; } else { node.delay = (node.delay * 1000 - 50) / 1000;; } if (node.delay <= 0) node.delay = 0; if (node.duration <= 0) node.duration = 0; } } this.cancelEvent(); }, // 拖拽游标的最大值 maxLeftOrMaxTime(type) { let nodeArr = []; this.componentArr.forEach((com) => { if (com['animation']['group'][0]['ani'].length) { com['animation']['group'][0]['ani'].map((val) => { if (type.indexOf('left') > -1) { nodeArr.push(10 * (val.duration * 1000 / 50) + 10 * (val.delay * 1000 / 50)); } else if (type.indexOf('time') > -1) { nodeArr.push(val.duration * 1000 + val.delay * 1000); } }) } }) return nodeArr.length > 0 ? () => { return Math.max(...nodeArr) || Math.max.apply(null, nodeArr); } : () => { return 0 }; }, // 时间轴游标拖动 changeSpot() { this.spotLeft = -10; this.left = 0; this.viewLeft = 0; document.onmousemove = (e) => { this.spotLeft = e.pageX <= 0 ? -10 : e.pageX >= this.maxLeftOrMaxTime('left')() ? this.maxLeftOrMaxTime('left')() : e.pageX; if (e.pageX > this.maxScrollLeft) this.left = e.pageX - this.maxScrollLeft >= this.maxScrollLeft ? this.maxScrollLeft : e.pageX - this.maxScrollLeft; if (e.pageX > this.viewMaxLeft) this.viewLeft = e.pageX - this.viewMaxLeft >= this.viewMaxLeft ? this.viewMaxLeft : e.pageX - this.viewMaxLeft; if (e.pageX <= 0) { this.left = this.viewLeft = 0; } } this.cancelEvent(); }, // 注销鼠标拖拽结束事件 cancelEvent() { document.onmouseup = (e) => { document.onmousemove = document.onmouseup = null; } }, // 选中组件 selectComponent(item, index) { this.curComponentIndex = index; this.$parent.$parent.getCoverage(item.uuid); }, // 删除组件动画 deleteComponent(item) { if (this.componentArr.length) { let idx = this.componentArr.indexOf(item); this.componentArr[idx].animation.group[0].ani = []; } }, // 播放组件动画 playComponentAnimation(item,name) { this.spotLeft = -10; let spotMaxLeft = 0; let time = 0; let start = null; let animationName = name ? name : item.animation.group[0].name; if(!name){ this.$parent.$parent.$refs.iframe.contentWindow.EditorSupport.root.previewAnimation(animationName); } if (item.animation.group[0].ani.length) { item.animation.group[0].ani.forEach((ani) => { spotMaxLeft += 10 * (ani.duration * 1000 / 50) + 10 * (ani.delay * 1000 / 50); time += ani.duration + ani.delay; }) //时间轴游标运动 let play = (t) => { if (!start) start = t; let progress = t - start; if (progress < time * 1000) { this.spotLeft = progress / 5; window.requestAnimationFrame(play); } else { this.spotLeft = spotMaxLeft; } } if (spotMaxLeft && time) { window.requestAnimationFrame(play); } } } } } 复制代码
时间轴到目前为止虽然实现了基本功能,可是并很差用,由于当时开发时间轴的时候,我对需求理解的也不是很到位。当时是参考smartslider3这个里面的时间轴的功能来写的。
遇到的问题很是的多,尤为我影响深入的是这样一个问题:
由于一个模板的数据很是的复杂,每一个模板里面有一个动画的数据。我在请求模板数据的时候,用了一个变量接受它。代码以下:
//this.Decrypt方法只是一个已经封装好了的数据解密方法 let res = JSON.parse(JSON.parse(this.Decrypt(result))); //打印出来二者不是相等的 console.log(this.Decrypt(result),res); 复制代码
第一个结果以下图所示:
第二个结果以下:
针对这个问题,我足足花了三个小时的时间来排查出问题所在,我依次打印出后台返回的数据都是没有问题的。
可是就是这么奇怪,它就是多了一个ease
对象,我真的很好奇,由于个人代码里确实没出现赋值代码,后面,我定位到引擎代码,结果还真的让我找到了问题所在。以下图所示:
由于引擎代码是经过webpack打包,而后我这边经过一个iframe标签来加载这个引擎代码。代码以下:
<iframe ref="iframe" class="iframe" src="../../../static/pixijs/index.html" frameborder="0" scrolling="no" @load="dataInit"></iframe> 复制代码
而后,我要建立一个引擎,就须要调用引擎代码提供的createApp
方法,参数就是数据以及width
和height
。代码以下:
//这里的this.addData就是前面所说的res this.$refs.iframe.contentWindow.Main.createApp(this.addData, 888, 500); 复制代码
这让我清晰的认识到了js对象
的引用特性。
固然还有一些项目,但都是比较小的项目,基本用到的技术都是jquery
之类的,而这一年的时间,我花在开发和维护发布后台管理系统的时间是最多的。但其它项目让我影响比较深入的仍是二天的时间完成的一个后台管理系统——报价后台管理系统。
报价后台管理系统所涉及到的功能很少,因此我完成的也算比较快,这没什么好说的。但我要总结到的就是在这个系统当中,我逐渐的规范化了代码。我将全部接口都规范在了一个文件里面,即api.js
。以下:
const host = ''; const api = { loginAPI:host + 'UserLogin/login',//登陆接口 registerAPI:host + 'UserLogin/addUser',//添加用户接口 exportAPI:host + 'MakeExcel/getJson',//普通用户导出数据接口 addSystemAPI:host + 'ShowProjectController/inserShowProject',//管理员添加系统接口 editSystemAPI:host + 'ShowProjectController/updateShowProject',//管理员编辑系统接口 deleteSystemAPI:host + 'ShowProjectController/deleteShowProject',//管理员删除系统接口 findSystemAPI:host + 'ShowProjectController/selectShowProject',//管理员与用户查询系统接口 lookWareApi: host + "EquipmentController/selectEqui", //查询软件和硬件信息接口 addWareApi: host + "EquipmentController/insertEqui", //添加软件和硬件信息接口 editWareApi: host + "EquipmentController/updateEqui", //修改软件和硬件信息接口 deleteWareApi: host + "EquipmentController/deleteEqui", //删除软件和硬件信息接口 modelApi: host + "ChildController/selectChild", //查软件与硬件型号 addModelApi: host + "ChildController/insertChild", //添加软件与硬件型号 updateModelApi: host + "ChildController/editChild", //修改软件与硬件型号 deleteModelApi: host + "ChildController/deleteChild", //删除软件与硬件型号 } export default api; 复制代码
慢慢的规范化了本身的代码,这才是我最想说的,这也是这个系统带给个人收获。
今年2月份到3月份个人主要工做也是完成发布系统的使用文档以及小程序的使用文档,采用gitbook
搭建的。也遇到了一些坑,都总结成了文章。详见。
工做之余闲下来以后,我就写本身的我的网站里的文档,不停的学习,目前已完成了HTML
与pixi.js
的总结,CSS
也快要完成了,但愿今年能把javascript
以及vue.js
完成。
若有兴趣可前往个人我的网站查看。
学如逆水行舟,不进则退。从2017年毕业到如今已经二年多了,其实我对本身的将来也仍是有些迷茫的,由于这些都不是我想要的,我特别想作一个自由职业者,可以作出一个别样的开源项目来,那才是个人目标,因此我用闲余时间作了一个ewplugins的开源项目,可是,这只是一个简单的开始,也是我对开源项目的初步涉猎,我相信我还有很长的路要走,给本身一句鼓励:加油,努力天天学习一点点,天天就进步一点点。