這是我參與更文挑戰的第10天,活動詳情查看: 更文挑戰前端
項目源碼node
偶然間我往webpack
標籤中發了一篇文章,碰巧看到了然叔寫的作了一晚上動畫,讓你們十分鐘搞懂Webpack,我並不瞭解然叔,但我以爲他極可能比我大,考慮到一位如此努力分享技術的大哥,爲了奉獻本身的光和熱,作了一晚上,那屬實讓人有些心疼,故我想盡個人一點力,爲大佬和你們們也發出個人一份熱。react
首先領略了一下文章,並印證了「不用想確定比我參悟深,大佬就是大佬」這個」偏見「,但最直接吸引我並非webpack
😂,而是一束「光」,對就是大佬寫了一晚上的動畫,我感嘆動畫製做的如此之精巧,表達效果居然如此之顯著,直接地,強烈地吸引了我。webpack
與其沉浸在喜好和膜拜的感受中,不如藉着這份情感作點事。我心中審視了一下本身現有的能力,很快就得出了一個想法💡,我好像能整出來一個作出這種「光」的輪子,ok,說幹就幹。git
首先我不想作成靜態資源那種的,好比視頻,gif之類的,這種是沒有「生命」的,只能稱之爲「物質」,我簡單用「陰」代指,我所須要是基於物質所煥發同時也能夠創造物質的的「生命,心靈」,我稱他爲陽。說ta是「活」的有點過,就是說能夠交互,能夠整合再利用,隨時能夠經過ta建立視頻,gif之類的靜態資源,豈不妙哉。github
首先做爲一個前端,技術面要打開,就很容易接觸到形形色色的技術,那麼渲染引擎就是之一。首先出於個人需求,經過渲染引擎開發這會大大的提升開發的效率,其次也能夠整合進我寫的腳手架「Moderate」中,是一個優解。web
我總但願用最簡單的話語,描述一件事,我比較喜歡簡單,那麼咱們就用簡單的方式把個人設計講講。markdown
視頻能夠快進,倒退,僅僅經過撥動進度條或者滑動屏幕便可,我喜歡這種交互,那就作成這樣,舒服。app
我但願設計出一系列獨立個體,能夠很好的串聯起整個邏輯,它具有了基本的功能,同時又具有了擴展的能力,可互相聯結,又彼此獨立,目前有兩個主要的個體,一個是單位個體entity
,另外一個是動做個體recation
ide
entity
大致應該具有如下行爲:lenPercent
和startPercent
live
process
end
entityArr
entity
recationArr
recation
,recation
大致應該具有如下行爲:start
和end
action()
那麼個體設計好了,圍繞個體所展開的邏輯,就瓜熟蒂落了。
代碼以下:
export default cc.Class({
extends: cc.Component,
properties: {
lenPercent: cc.Float,
startPercent: cc.Float,
isAutoStart: cc.Boolean,
entityArr: {
default: [],
type: cc.Node
}
},
//externalDuration:外部時間(父節點傳過來的時間),由父節點決定
//internalDuration:本身內部定的時間,有本身決定,
//爲何要區分兩個呢?因爲外部應該只能肯定個人播放時間,不該該決定個人播放速率,然後者應該有個體自身決定,
//startTime和endTime:由父節點指定的開始和結束時間,(根據父節點的世界定的‘外部時間’!!!)
//timeLine-表示時間到哪了,(根據父節點的世界定的‘外部時間’!!!)
//totaTime-表示我在父節點應該播放的總時長,(根據父節點的世界定的‘外部時間’!!!)
//progressValue就是經過父節點傳過來的timeLine,totaTime,timeLine得出我處於的播放進度百分比
//相應的往本身的子節點傳的就得參照本身的
ctor() {
this.isLive = false;
this.startTime = undefined;
this.endTime = undefined;
this.internalDuration = 0;//個體內部的時長
this.externalDuration = 0;//個體相對父級的時長
this.progressValue = 0;
this.entryData = [];
this.recationArr = [];
this.startPosition = cc.v2();
this.entityArrEx = [];
},
// LIFE-CYCLE CALLBACKS:
start() {
this.startPosition = this.node.position;
},
onLoad() {
this.node.comName = this.__classname__;
this.internalDuration = this.node.getContentSize().height;
//防止設置的時間太長,強制設置爲剩餘的時長
if (this.lenPercent + this.startPercent > 1) {
this.lenPercent = 1 - this.startPercent;
}
if (this.isAutoStart) {
this.startPercent += Math.abs((this.node.position.y / this.node.parent.getContentSize().height));
}
},
onEnable() {
let self = this;
if (this.entityArr.length) {
this.entityArrEx = this.entityArr.map((item, index) => {
let entity = item.getComponent(item._name);
if (entity.isAutoStart) {
}
this.entryData.push(entity.initData({
startTime: this.getStarTime(entity.startPercent),
totaTime: self.internalDuration,
}));
return entity;
});
}
},
//業務接口
getStarTime(value) {
if (value <= 1) {
return value * this.internalDuration
} else {
return value
}
},
initData({ totaTime, startTime }) {
this.startTime = startTime;
this.externalDuration = this.lenPercent <= 1 ? totaTime * this.lenPercent : this.lenPercent;
//結束時間最大隻能是父類節點結束時間
//由於父節點結束,子節點也必須結束
this.endTime = Math.min(totaTime, this.startTime + this.externalDuration);
return {
startTime: this.startTime,
internalDuration: this.internalDuration,
endTime: this.endTime
}
},
getCurrentTime(percent) {
return (
this.startTime + (percent <= 1 ? this.externalDuration * percent : percent)
);
},
live() {
this.isLive = true;
},
calcProgress() {
this.progressValue = (this.timeLine - this.startTime) / this.externalDuration;
},
calcReactionProgress({ start, end }) {
start = (start <= 1) ? this.internalDuration * start : start;
end = (end <= 1) ? this.internalDuration * end : end;
return Math.min((this.progressValue * this.internalDuration - start) / (end - start), 1);
},
process({ timeLine }) {
this.timeLine = timeLine;
this.calcProgress();
this.internalTimeLine = this.progressValue * this.internalDuration;
let actionArr = this.recationArr.filter((item) => {
if (item) {
let isOk = (timeLine > this.getCurrentTime(item.start) &&
timeLine <= this.getCurrentTime(item.end)) ||
(!item.start && !item.end)
if (isOk) {
item.isAction = true
} else {
if (item.isAction) {
item.action(this.calcActionData(item, true))
}
item.isAction = false
}
return isOk;
}
});
actionArr.forEach((item) => {
item.action(this.calcActionData(item));
});
},
update() {
let self = this;
this.actionEntityArr = this.entityArrEx.filter((entity) => {
if ((self.internalTimeLine) > entity.startTime && self.internalTimeLine <= entity.endTime) {
if (!entity.isLive) {
entity.live();
}
entity.process({
timeLine: self.progressValue * self.internalDuration,
});
return true;
} else {
if (entity.isLive) {
entity.end();
}
}
return false;
});
},
calcActionData(item, isEnd) {
let params = {};
let actionLen = (item.end - item.start) || 1;
let progress;
progress = Math.min((this.progressValue - item.start) / actionLen, 1);
if (isEnd) {
let isEndForce = window.GLOBAL.dir > 0;
let isEndForceStart = window.GLOBAL.dir < 0;
if (isEndForce) {
progress = 1
} else if (isEndForceStart) {
progress = 0
}
params = {
isEndForce: isEndForce,
isEndForceStart: isEndForceStart
}
}
params = {
actionLen,
progress,
...params,
...item
}
return params;
},
end() {
this.isLive = false;
//若是滑動很是快,而且是快進而非後退,那麼就要直接強行設置反饋爲結束
// if (window.GLOBAL.dir > 0) {
// }
this.recationArr.forEach(item => {
if (item.isAction) {
item.isAction = false
item.action(this.calcActionData(item, true))
}
});
},
});
複製代碼
這是然哥的作的動畫
個人版本(作的時候才發現這倆是一體的)
就在我開始着手作然叔第三個動畫,摸索箭頭如何實現的時候,機緣巧合地我有了另外一個新的靈感,我作出了這個。
「Moderate」新首頁。
感謝然叔。
這是我爲「Moderate」寫專欄的第十篇了,頗有成就感,雖然不受歡迎,但整個過程我是快樂的。我碰見了好多好多熱情的,才華的,可敬的coder和掘友,收穫良多。最後由衷地感謝掘金營造的社區氛圍,感嘆相見恨晚,但一見如故,自強不息,將來可期。