• 年底了,接个大活儿,做一个回顾公司五年发展的总结ppt,要求做成H5网页


    公司想做个五年总结

    这不快年底了么,公司高层打算把这五年的发展历程做一次回顾巡礼,一方面宣扬一下公司文化,另一方面歌颂一下公司这五年来取得的辉煌成就,单纯的做个海报,写个公众号文章,或整个传统ppt在内部宣讲,再剪个视频啥的已经无法满足公司想众乐乐的强烈情感了。

    于是想做一个交互性良好、内容表达形式多样、章节清晰、条理分明、易于传播的“ppt”,于是在公司千人大群里号召,有没有谁能做的,问了好几次,最后只有我应下了这个大活儿。

    最后我与一位主要负责这件事的领导(我称为带头大哥)和一位UI小姐姐临时组成了一个小队,来完成这项任务。

    在线观看地址,可以先看看效果。记得手指或者鼠标,向上滑动来播放)

    很快就确定了中心思想,并设计了一套。。。。对联?

    带头大哥没过几日就发我一个文档,上面写了对仗工整,思想明确的“对联”

    1. 重返这五年,重温那岁月
    2. 成长多措举,管理提格局
    3. 工作造氛围,企学见雏形
    4. 发展聚人才,规模逐壮大
    5. 搜索寻突破,拓展创业务
    6. 那年启征程,改变从此始
    7. 种得梧桐树,凤凰自然来
    8. 全方位服务,码农心无骛
    9. 创新谋发展,突破新技术
    10. 环境不断好,员工感幸福
    11. 乘势迎挑战,赋能于行业
    12. 磨砺积能力,自研得硕果
    13. 逆境砥砺行,收入节节高
    • 我:哇撒~,领导,这写的很好嘛~,这每一页不做个动画都说不过去啊。
    • 领导:对,就打算这么做。
    • 我:哈哈哈哈,收到,那么具体做成什么样呢?
    • 领导:em~,我已经安排UI小姐姐开始设计了,过一阵就能出图。
    • 我:哦哦哦,好的~

    领会精神的设计小姐姐就开始陆续出图了~~~

    • 领导:阿强,图做好了,你看一下。
    • 我:ok,哇偶这图可以啊。
    • UI小姐姐:有什么问题,需要切图啥的尽管跟我说。
    • 我:好的好的。
    • 领导:阿强,那么我们开始研究如何做吧,要求是要做成H5,这样可以方便分享传阅,而且时间我们只有五天,我们要尽快了。
    • 我:五天???做不完咋办
    • 领导:你知道的,deedline不是我定的,而且这是👆面看中的亲儿子项目,献礼用的,所以。。。
    • 我:懂了,我搏一把,目前来看用游戏引擎最合适,毕竟画面表现这块,用引擎更好一点。
    • 领导:可以,具体设计方面到时我们再一起商量怎么做,开发这块需要的话,我也可以参与进来,毕竟我也接触过白鹭引擎,对了,你打算用啥写?
    • 我:cocos creater
    • 领导:嗯嗯,没问题,有什么我能帮忙的尽管跟我说,最后能做出来就行
    • 我:好的,收到。

    于是我做了一个工具

    so,时间这么紧,而且做不完可能会有不小的后果,em~~~~,并且这件事也不是着急干就能干完的,我需要冷静的想一个办法,我忽然想到《代码整洁之道》中好像说过,具体记不清,大体意思是:“越是想快,就要写好代码”。

    em~~~,好的,那么我拿出几天设计一个工具吧,不需要多么成熟,只要能够满足容易上手,可以批量制作就行,这样就能够实现人手的增加,效率也会提高的局面,到时完成就变得很有戏了,就这么办。

    于是我用了两三天的时间做出了这个工具,并做了一个demo给到了带头大哥,他表示ok,还可以,能接受,而且经过我的指导,他也可以参与进来了,于是我们就开始“愉快地”批量的制作了。

    工具的开发思路

    我总希望用最简单的话语,描述一件事,我比较喜欢简单,那么我们就用简单的方式把我的设计讲讲。

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

    视频可以快进,倒退,仅仅通过拨动进度条或者滑动屏幕即可,我喜欢这种交互,那就做成这样,舒服。 首先我不想做成静态资源那种的,比如视频,gif之类的,这种是没有“生命”的,只能称之为“物质”,我简单用“阴”代指,我所需要是基于物质所焕发同时也可以创造物质的的“生命,心灵”,我称他为阳。说ta是“活”的有点过,就是说可以交互,可以整合再利用,随时可以通过ta创建视频,gif之类的静态资源,岂不妙哉。

    设计好个体,非常关键

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

    entity大体应该具备以下行为:

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

    recation大体应该具备以下行为:

    • 描述自身运行的周期:startend
    • 执行的动作:action()

    那么个体设计好了,围绕个体所展开的逻辑,就顺理成章了。

    代码如下:

    1. export default cc.Class({
    2. extends: cc.Component,
    3. properties: {
    4. lenPercent: cc.Float,
    5. startPercent: cc.Float,
    6. isAutoStart: cc.Boolean,
    7. entityArr: {
    8. default: [],
    9. type: cc.Node
    10. }
    11. },
    12. //externalDuration:外部时间(父节点传过来的时间),由父节点决定
    13. //internalDuration:自己内部定的时间,有自己决定,
    14. //为什么要区分两个呢?由于外部应该只能确定我的播放时间,不应该决定我的播放速率,而后者应该有个体自身决定,
    15. //startTime和endTime:由父节点指定的开始和结束时间,(根据父节点的世界定的‘外部时间’!!!)
    16. //timeLine-表示时间到哪了,(根据父节点的世界定的‘外部时间’!!!)
    17. //totaTime-表示我在父节点应该播放的总时长,(根据父节点的世界定的‘外部时间’!!!)
    18. //progressValue就是通过父节点传过来的timeLine,totaTime,timeLine得出我处于的播放进度百分比
    19. //相应的往自己的子节点传的就得参照自己的
    20. ctor() {
    21. this.isLive = false;
    22. this.startTime = undefined;
    23. this.endTime = undefined;
    24. this.internalDuration = 0;//个体内部的时长
    25. this.externalDuration = 0;//个体相对父级的时长
    26. this.progressValue = 0;
    27. this.entryData = [];
    28. this.recationArr = [];
    29. this.startPosition = cc.v2();
    30. this.entityArrEx = [];
    31. },
    32. // LIFE-CYCLE CALLBACKS:
    33. start() {
    34. this.startPosition = this.node.position;
    35. },
    36. onLoad() {
    37. this.node.comName = this.__classname__;
    38. this.internalDuration = this.node.getContentSize().height;
    39. //防止设置的时间太长,强制设置为剩余的时长
    40. if (this.lenPercent + this.startPercent > 1) {
    41. this.lenPercent = 1 - this.startPercent;
    42. }
    43. if (this.isAutoStart) {
    44. this.startPercent += Math.abs((this.node.position.y / this.node.parent.getContentSize().height));
    45. }
    46. },
    47. onEnable() {
    48. let self = this;
    49. if (this.entityArr.length) {
    50. this.entityArrEx = this.entityArr.map((item, index) => {
    51. let entity = item.getComponent(item._name);
    52. if (entity.isAutoStart) {
    53. }
    54. this.entryData.push(entity.initData({
    55. startTime: this.getStarTime(entity.startPercent),
    56. totaTime: self.internalDuration,
    57. }));
    58. return entity;
    59. });
    60. }
    61. },
    62. //业务接口
    63. getStarTime(value) {
    64. if (value <= 1) {
    65. return value * this.internalDuration
    66. } else {
    67. return value
    68. }
    69. },
    70. initData({ totaTime, startTime }) {
    71. this.startTime = startTime;
    72. this.externalDuration = this.lenPercent <= 1 ? totaTime * this.lenPercent : this.lenPercent;
    73. //结束时间最大只能是父类节点结束时间
    74. //因为父节点结束,子节点也必须结束
    75. this.endTime = Math.min(totaTime, this.startTime + this.externalDuration);
    76. return {
    77. startTime: this.startTime,
    78. internalDuration: this.internalDuration,
    79. endTime: this.endTime
    80. }
    81. },
    82. getCurrentTime(percent) {
    83. return (
    84. this.startTime + (percent <= 1 ? this.externalDuration * percent : percent)
    85. );
    86. },
    87. live() {
    88. this.isLive = true;
    89. },
    90. calcProgress() {
    91. this.progressValue = (this.timeLine - this.startTime) / this.externalDuration;
    92. },
    93. calcReactionProgress({ start, end }) {
    94. start = (start <= 1) ? this.internalDuration * start : start;
    95. end = (end <= 1) ? this.internalDuration * end : end;
    96. return Math.min((this.progressValue * this.internalDuration - start) / (end - start), 1);
    97. },
    98. process({ timeLine }) {
    99. this.timeLine = timeLine;
    100. this.calcProgress();
    101. this.internalTimeLine = this.progressValue * this.internalDuration;
    102. let actionArr = this.recationArr.filter((item) => {
    103. if (item) {
    104. let isOk = (timeLine > this.getCurrentTime(item.start) &&
    105. timeLine <= this.getCurrentTime(item.end)) ||
    106. (!item.start && !item.end)
    107. if (isOk) {
    108. item.isAction = true
    109. } else {
    110. if (item.isAction) {
    111. item.action(this.calcActionData(item, true))
    112. }
    113. item.isAction = false
    114. }
    115. return isOk;
    116. }
    117. });
    118. actionArr.forEach((item) => {
    119. item.action(this.calcActionData(item));
    120. });
    121. },
    122. update() {
    123. let self = this;
    124. this.actionEntityArr = this.entityArrEx.filter((entity) => {
    125. if ((self.internalTimeLine) > entity.startTime && self.internalTimeLine <= entity.endTime) {
    126. if (!entity.isLive) {
    127. entity.live();
    128. }
    129. entity.process({
    130. timeLine: self.progressValue * self.internalDuration,
    131. });
    132. return true;
    133. } else {
    134. if (entity.isLive) {
    135. entity.end();
    136. }
    137. }
    138. return false;
    139. });
    140. },
    141. calcActionData(item, isEnd) {
    142. let params = {};
    143. let actionLen = (item.end - item.start) || 1;
    144. let progress;
    145. progress = Math.min((this.progressValue - item.start) / actionLen, 1);
    146. if (isEnd) {
    147. let isEndForce = window.GLOBAL.dir > 0;
    148. let isEndForceStart = window.GLOBAL.dir < 0;
    149. if (isEndForce) {
    150. progress = 1
    151. } else if (isEndForceStart) {
    152. progress = 0
    153. }
    154. params = {
    155. isEndForce: isEndForce,
    156. isEndForceStart: isEndForceStart
    157. }
    158. }
    159. params = {
    160. actionLen,
    161. progress,
    162. ...params,
    163. ...item
    164. }
    165. return params;
    166. },
    167. end() {
    168. this.isLive = false;
    169. //如果滑动非常快,并且是快进而非后退,那么就要直接强行设置反馈为结束
    170. // if (window.GLOBAL.dir > 0) {
    171. // }
    172. this.recationArr.forEach(item => {
    173. if (item.isAction) {
    174. item.isAction = false
    175. item.action(this.calcActionData(item, true))
    176. }
    177. });
    178. },
    179. });
    180. 复制代码

    整体思路简单说

    • 就是设置一个进度条,通过触摸屏幕进行前进和后退。
    • 设置每一part的时间占比,然后串联起来。
    • 每一个part内部,也设置内部节点的运动的事件占比,以及具体做什么运动。
    • 然后根据定时循环,判断当前时刻应该执行节点的是哪个,执行的节点该执行的运动是哪个

    就这么简单。

    这个思路完全可以使用在dom上,完全可以用react和vue实现,我会陆续重构完毕。

    我跟领导就开始设计怎么做了

    开场

    • 领导:阿强,这个就是我们项目的开场。

      • 第一张就是首页图,然后过一会自动转场。
      • 第二张就是过场之后显示的。
    • 我:哦哦,懂了,话说,怎么转场呢?

    • 领导:看到小飞机没有~

      • 就这个。

      • 还有第二页的这个。

      • (A):第一页上来,左下角一个人坐着飞机向上飞,然后小飞机往右上角飞,飞离出去之后,开始转场到二张。

      • (B)然后第二页左下飞入这个小飞机,飞到中心处,也就是图片上的位置,那么开场part就ok了。)

    • 我:ok,明白了,开整。

    实现方式:

    A实现:

    目标就是让这个节点移动一段距离停下来,那么写一个脚本plane0_1,上来就执行一个移动一段距离的动作。

    1. import entity from '../../base/entity';
    2. cc.Class({
    3. extends: entity,
    4. properties: {
    5. },
    6. // LIFE-CYCLE CALLBACKS:
    7. onLoad() {
    8. this._super();
    9. let self = this;
    10. // 就是上来,就执行一个运动,moveBy就是一段时间,移动移动距离的动画api
    11. var action = cc.moveBy(1, cc.v2(this.node.getContentSize().width + 100, 200))
    12. // 执行动作
    13. this.node.runAction(action);
    14. },
    15. });
    16. 复制代码

    挂载到节点上就可以了。

    这里简单介绍一下,我设置了几个参数:

    • lenPercent:就是节点运动的总时长
    • startPercent:就是运动的开始时刻
    • isAutoStart:就是自动启动,无视startPercent的设置
    • entityArr:就是复制管理的节点,这样自己的运动周期内就能操作其关联的节点的运动。

    B实现:主要就是实现这个节点移动飞行

    那么根据上面描述的思路,我们不难实现这个小飞机的移动,无非就是设置这个小飞机在这个part里的开始时刻是多少,运动多久,运动到哪,只要把这些定好,动画自动产生。

    那么开发脚本

    1. import entity from '../../base/entity';
    2. cc.Class({
    3. extends: entity,
    4. properties: {
    5. plane: cc.Node
    6. },
    7. // LIFE-CYCLE CALLBACKS:
    8. onLoad() {
    9. this._super();
    10. let self = this;
    11. // 向右上角移动一段距离
    12. var action = cc.moveBy(1, cc.v2(this.node.getContentSize().width + 500, 500))
    13. // spawn是同时执行运动的函数,目的是让moveBy运动结合一个缩小的运动,这样,有种越发越远,又越小的视觉感。
    14. let action2 = cc.spawn(action, cc.scaleTo(1, 2))
    15. // 执行动作
    16. this.node.runAction(cc.sequence(cc.delayTime(1), action2, cc.callFunc(() => {
    17. self.node.active = false;
    18. self.node.parent.active = false;
    19. self.plane.runAction(cc.moveTo(1, cc.v2(0, 0)))
    20. })));
    21. },
    22. });
    23. 复制代码

    那么下面整体实现,基本都是依靠上面这些思路,实现的,单纯的开始套就能实现下面的动画了。 是不是很神奇,这套思路,完全可以套用到react和vue上。

    • 整好了,领导~,看下效果

    • 领导:行,可以,就这样。

    part1

    • 重返这五年,重温那岁月

    • 领导:这张呢~

    • 我:坐飞机的小女孩往上飞出去,做小角的男人飞入画面

    • 领导:差不多,不过坐小飞机的小女孩这块,应该再丰富一点。

      • 记得我们刚才说的那个小飞机没,这个小女孩飞的时候,渐渐的变小,有种往远飞的感觉,然后变成小飞机飞走~
    • 我:哦哦哦,懂了,开整

      • 整好了,领导~,看下效果

    • 领导:行,可以,就这样。

    part2

    • 成长多措举,管理提格局
    • 工作造氛围,企学见雏形

    • 我:这个要做成啥样?

    • 领导:入场就是从右边缓缓进来,还有看到这个没

      • 这些圆片一点一点的滑入屏幕,然后屏幕出现内容

      • 这张图

        • 就是屏幕上一点一点出现内容,然后人物一点一点入画。
    • 我:懂了,开整

      • 整好了,领导~,看下效果

    • 领导:对,是这样。

    part3

    • 发展聚人才,规模逐壮大
    • 搜索寻突破,拓展创业务

    • 领导:这个主要想表达的就是公司不断的壮大,业务上不断的突破,所以要有种过程感,你看着发挥吧
    • 我:收到,我试试
      • 整好了,领导~,看下效果

    • 领导:嗯,很不错,可以可以,有过程的画面感。
      • 日历还能动啊,而且日历动的同时,后面的背景一点一点的显示,也寓示了我们的办公楼越来越大,可以,有细节,不错。
      • 过场加入了云彩变大,切换很自然嘛。

    part4

    • 那年启征程,改变从此始

    • 领导:这个该咋设计呢?你看飞机这么多,我们能让这些飞机各飞各的么?

    • 我:可以啊,我试试

      • 整好了,领导~,看下效果

    • 领导:可以的,就这样吧。

    part5

    • 种得梧桐树,凤凰自然来

    • 领导:这个柱状图能不能表现出一点一点增长的效果,还有向上箭头能不能也可以有个升的过程
      • 还有这个拿笔的人物

        • 可不可以有种书写感,就是表现出在写字
    • 我:哦哦哦,我试试,应该可以,针对这些情况,我再丰富几种表现手段
      • 整好了,领导~,看下效果

    • 领导:嗯,可以,就是这效果。

    part6

    • 全方位服务,码农心无骛
    • 创新谋发展,突破新技术

    • 领导:这就可以自由发挥了,用一个合理的方式一点一点介绍就好。
    • 我:懂,开整
      • 整好了,领导~,看下效果

    • 领导:嗯,很不错嘛,有ppt的感觉,挺好的
    • 我:哈哈哈,确实有点。

    part7

    • 环境不断好,员工感幸福
    • 乘势迎挑战,赋能于行业

    • 领导:第一张就是简单的显隐就可以,主要是第二张的这个

      • 这个,我跟UI小姐姐说了,希望做成一个列表,但小姐姐做成了这种,那么就需要滚动了。
    • 我:就像滚动页面那样,哦哦哦,我明白了,开整。

      • 整好了,领导~,看下效果

    • 领导:行,可以的,就这样。

    part8

    • 磨砺积能力,自研得硕果

    • 领导:阿强,这个主要是表现我们去的成就,我们仅仅把文字凸显出来就好,其他就不要求了

      • 但,我觉得第一幅图,还是可以做点文章的,他表示我们获得的证书,你看看能不能实现,物理下落,然后一本本摞起来的效果
    • 我:哈哈哈,好的,我试试,效果我尽量做,可能物理效果没那么像,但摞起来的效果肯定是有的。

    • 领导:可以,你试试

    • 我:好的,开整。

      • 整好了,领导~,看下效果

    • 领导:辛苦了,阿强,再加把劲,我们要成功了。

    • 我:好的~~~下一个就是结尾了吧,加油~

    尾声

    • 逆境砥砺行,收入节节高

    • 领导:阿强这个结尾,我们收好,我们要有种意境,这样就有感觉,你能懂么
    • 我:我懂你
    • 领导:你看奥第一幅和第二幅区别,是一个没有河水,一个有河水,还有就是一个有没破晓,一个有日出
      • 你看你能衔接好,这可是一个艰巨的任务,干完这个我们就胜利了
    • 我:好的领导,我一定完成任务
      • many hours later

      • 可算整好了,我的大哥~,效果我是尽力了。

    • 领导:干的漂亮,阿强,有意境了,非常不错,辛苦了。
    • 我:嘿嘿,马马虎虎,我尽力了,我打包好就上线吧。
    • 领导:好的好的,辛苦了。

    这就是在线预览地址,可以看看效果。

    结尾

    目前项目仅仅用了5天时间就出来了个雏形,还很粗糙,很多地方可以进一步的优化。

    这仅仅是一个开始,未来,我会使用react或者vue3,整一个lowcode制作工具,这样就可以更加的方便制作了。

    然后开源,敬请期待。

    有问题随时交流,我建立一个小清晰qq群,叫“闲D岛”,技术问答群,有问必答,有兴趣可以加群号:551406017

    题外话

    有一说一,我比较低调,一般出风头的事儿,我是没啥想法的,主要是我觉得争名逐利的画面太尴尬,我来不了这个,但也不知我那天是怎么了,当群里问了好几次都没人回应,我就来了点脾气,即然没谁上,那我试试吧。就这样我接下了这个任务,现在再想,可能就是因为这点脾气,我才敢去做的,也许有点莽了,如果没做成呢?哈哈哈,那就不能想了,还好我实现了,我让这个脾气变得更像勇气了,有时候真不妨大胆一点,觉得自己可以,那就试一试,真没准行呢,别怕输,输丢什么人,怕才丢人呢。

  • 相关阅读:
    linux进程概念(下)
    分割集合list成为多个子list
    .net----委托和事件
    Windows MFC 工程应用开发与框架原理完全剖析视频教程(上)
    基础1:JS的原型和原型链究竟是什么
    【Rust指南】快速入门|开发环境|hello world
    Python之三大基本库——Numpy(1)
    高斯混合滤波
    SpringBoot 中的 Liquibase 适配达梦数据库(DM)和 Flowable 工作流
    LeetCode算法位运算—只出现一次的数字
  • 原文地址:https://blog.csdn.net/BASK2312/article/details/127961495