我作了一个制做代码演示动画的工具,答应我之后别再熬夜作动画了

这是我参与更文挑战的第10天,活动详情查看: 更文挑战前端

项目源码node

作这个工具的动机

偶然间我往webpack标签中发了一篇文章,碰巧看到了然叔写的作了一晚上动画,让你们十分钟搞懂Webpack,我并不了解然叔,但我以为他极可能比我大,考虑到一位如此努力分享技术的大哥,为了奉献本身的光和热,作了一晚上,那属实让人有些心疼,故我想尽个人一点力,为大佬和你们们也发出个人一份热。react

我被吸引了

首先领略了一下文章,并印证了“不用想确定比我参悟深,大佬就是大佬”这个”偏见“,但最直接吸引我并非webpack😂,而是一束“光”,对就是大佬写了一晚上的动画,我感叹动画制做的如此之精巧,表达效果居然如此之显著,直接地,强烈地吸引了我。webpack

行动起来

与其沉浸在喜好和膜拜的感受中,不如借着这份情感作点事。我心中审视了一下本身现有的能力,很快就得出了一个想法💡,我好像能整出来一个作出这种“光”的轮子,ok,说干就干。git

描绘出本身的指望就成功了一半

首先我不想作成静态资源那种的,好比视频,gif之类的,这种是没有“生命”的,只能称之为“物质”,我简单用“阴”代指,我所须要是基于物质所焕发同时也能够创造物质的的“生命,心灵”,我称他为阳。说ta是“活”的有点过,就是说能够交互,能够整合再利用,随时能够经过ta建立视频,gif之类的静态资源,岂不妙哉。github

基于指望我肯定了实现方案

首先做为一个前端,技术面要打开,就很容易接触到形形色色的技术,那么渲染引擎就是之一。首先出于个人需求,经过渲染引擎开发这会大大的提升开发的效率,其次也能够整合进我写的脚手架“Moderate”中,是一个优解。web

设计开发

我总但愿用最简单的话语,描述一件事,我比较喜欢简单,那么咱们就用简单的方式把个人设计讲讲。markdown

经过鼠标或触摸滑动,推动动画进程

视频能够快进,倒退,仅仅经过拨动进度条或者滑动屏幕便可,我喜欢这种交互,那就作成这样,舒服。app

设计好个体,很是关键

我但愿设计出一系列独立个体,能够很好的串联起整个逻辑,它具有了基本的功能,同时又具有了扩展的能力,可互相联结,又彼此独立,目前有两个主要的个体,一个是单位个体entity,另外一个是动做个体recationide

entity大致应该具有如下行为:

  • 描述自身运行的周期: lenPercentstartPercent
  • 生命周期函数
    • 开始:live
    • 渲染:process
    • 结束:end
  • 能够装载其余个体的能力:entityArr
    • 组成单位为:entity
  • 执行动做的集合:recationArr
    • 组成单位为:recation,

recation大致应该具有如下行为:

  • 描述自身运行的周期:startend
  • 执行的动做: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和掘友,收获良多。最后由衷地感谢掘金营造的社区氛围,感叹相见恨晚,但一见如故,自强不息,将来可期。

相关文章
相关标签/搜索