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的開源項目,可是,這只是一個簡單的開始,也是我對開源項目的初步涉獵,我相信我還有很長的路要走,給本身一句鼓勵:加油,努力天天學習一點點,天天就進步一點點。