钉子の次元

Dimpurr – 千里之行,始於足下。

校内应用个性化数据年报项目全程吐槽纪实 – 长单页面前端工程与 ECharts 图表可视化

随着校区从昌平宏福迁往西土城,一边为终于能够河北人进京城感到高兴,一边为入住人称将军冢的我校 1955 年最好的宿舍而担忧,喜忧参半的在陌生的新宿舍用 Axure 画着学院内部项目交互原型的自己,迎来了大学的第一个寒假。

恶补 React 和 ES6 知识、补习高数预习离散数学和入门 TensorFlow、组织和培训学院内团队的新人们入门 Web 前后端、在 VJudge 上参与面向 ACM 新生的 BUPT Winter Training 练习赛,前半个寒假就这么飞快的过去了。之后回到老家过年的我,除了享受和可爱妹妹和其他亲戚的团聚,其他时间就花在了这么一个 —— 类似网易云个人年报,展示校内自建应用的全局统计和用户个性化数据的单页面年报页面上。

校内应用个性化数据年报 设计稿/代码实现/图表效果

校内应用个性化数据年报 设计稿/代码实现/图表效果

当时接到这个语焉不详的任务要求之后我其实是一脸懵逼的,而且这个明显重策划设计而非技术的任务,最开始居然只安排了我和另一位(虽然非常靠谱的)后端负责。于是只好抄刀硬上,自己当策划,出了一版初步设计稿就开始动工前端,然后再想办法拜托组里的设计师们修改设计图和撰写文案。后来转念一想,虽然这个估计写出来也没多少人看 (误) 的任务不是不能随便简单点糊弄过去,但是不如也干脆作为一个重新练习快要生疏的静态页面相关前端技术们的机会。最后的结果,大概就是在一个不算很复杂的移动端长页面上,塞进去了没有必要程度的自觉好玩想复习或尝试一下的技术吧。

这篇文章就以全程实录的方式,记述了我在接到这个校内应用个性化数据年报单页面的任务之后,从初期策划和出设计稿、组织文案撰写,再到前端工程开发、处理应用统计数据、利用 ECharts 绘制可视化图表,到最后部署上线的完整过程,以及在此期间内心满满的槽点。不得不说,一个春节在老家县城天天抱着 Surface 出来找咖啡馆干活也是没谁了,而且,这些校内应用用户数据存量稀薄的凄惨现实(导员别打我 233),实在有点让人难以正确的进行小数据可视化 ……

那么,也许你可以从本文窥见一个完整有趣的项目流程,或者了解我在项目过程中收获到的经验;或者你发现了其中我犯的错误或不足,也请不吝赐教。以下。

任务开始

大一上期末考完的那天下午,打开微信,就收到了 FredericDT 同学的消息:

FredericDT:
考完了没! 易班有活干! 【滑稽】
2017年年报 撸个酷炫的 h5 presentation

Dim:
今天刚考完
这玩意儿我不太熟悉,不过能做,任务是怎么分配下来的?你要不去群里问问有没有哪个组有熟悉 h5pre 或者 impree.js 之类的人,做起来效率会更高
如果没有或者分下来是要我们组做我可以调研下相关服务,或者 impressjs 撸一个
也不难,主要要点设计成本和文案,我没经验就是了

FredericDT:
得撸出来 得能干活 ddl大年三十 我在撸接口

Dim:
所以这年报成员有谁,文案谁定,设计谁做,代码谁写,接口是啥
这个基本都是设计和文案成本,代码逻辑没啥吧

FredericDT:
明天说详细内容 你把问题问出来 我明天一一解答 成员就咱们组 “要能干活的人”

于是,洗了个澡之后,我就在 Typora 上打出了问题清单:

项目规划问题清单 (Typora)

项目规划问题清单 (Typora)

关于易班年报H5PRE

* 个人理解:制作一个富交互动画演示单页面,优先支持手机(主要浏览渠道:微信分享,微信内置浏览器),顺便支持或者不支持桌面?
* 预期制作流程:
    * 专人负责策划。思考演示内容和主题是什么,展示易班些什么,撰写每一页的标题、介绍文案,并提供配图主题方向。本质上核心和最麻烦的工作。
    * 设计支持。修订处理演示中需要的素材图片,制作或者绘制一些需要的小装饰元素图标等。
    * 代码支持。制作实现网页网页,能够在易班渠道发布和观看。如果能找到现成的H5页面制作工具则为低端工作,若有负责交互逻辑、需要桌面端支持、需要效果很现代或者有动态程序逻辑等则会较复杂。
* 综上,问题有:
    * 该H5预期的【观看对象和目标人群】是?
    * 该H5希望展示的【具体内容】,目前以及有哪些想法?
    * 该H5的【风格和主题】是?【体现的精神风貌】是?
    * 该H5【支持移动端】还是【支持桌面端】?【发布和推广的渠道】是?
    * 有无擅长该类演示【文案策划和美术设计的人才】可以参与?【唯独程序员我们不缺】。

不久后,得到了答案:

* 广大学生
* 主题为“沙邮易班功能推荐”,对用户展示其活跃使用的功能(范围为全部北邮维护的应用)及频次,对其未使用功能进行推荐
* 现代科技风格,带有拜年的意味
* 都要支持。部署在易班的机子上
* 我不熟...拉不到人

很快回复:

Dim:
「* 有无擅长该类演示【文案策划和美术设计的人才】可以参与? 我不熟...拉不到人」
这个我这两天想办法解决下,解决不了那就这么开工了

FredericDT:
行 没有就是你 你懂得 【滑稽】
我30号前尽量撸几个应用的接口出来

几天后,回到家,我也正式开工了。干脆还是直接贴记录吧:

Dim:
h5pre 那边情况如何 是不是该动工了

FredericDT:
定位是“功能推荐” 按功能把纵向的 section 码出来
我今晚给你出全部的功能点(中文描述) 我写成 google spreadsheet

Dim:
现在PRE应该是两部分吧 上来一个欢迎屏幕 下滚,然后是功能推荐 下滚,是数据可视化
然后你给我功能推荐列个点,数据可视化列个点(数据可视化你这边是还没琢磨好是吗
你可以把能导出,或者做好的数据都给我看看(直接截图或者txt/csv给我),我也试着想想

FredericDT:
数据嘛...我得看看数据库的结构

Dim:
那我现在就动工,把页面的框架搭一下,随便写点废话文案,准备做功能推荐部分咯 数据部分最后再想办法加
反正就是个复杂单页,我正好练练手,给新个人页做准备 - -
对了,咱们这个年报是大众化还是个人化 要给每个用户自己个性化数据? 类似网易云那种

FredericDT:
个人化 对 类网易云

Dim:
我觉得不合适,咱们用户覆盖率太低,比如我肯定就没有任何数据
个人化的可以有 全部堆起来放一个section 全局的搞多一点 占面积大一点

FredericDT:
针对你这种用户 就是功能推荐 针对有数据的就是年报

Dim:
不能做一个全部用户的大数据(不大)分析吗
比如,今年,205个用户在易健身预约了14832次健身 50%的人在下午18:00~20:00时段来健身房 咕咕咕占比20%,希望大家要履行承诺哦 这种感觉 然后给个预约时段的饼图,预约人数的曲线
预约人数的曲线,可以在拐点发掘点东西,比如「2月14日情人节,健身人数较少,同学们都在做什么呢 ...?」

FredericDT:
可以做 这个可以有 这种感觉不错 我这个接口就是这么整出来的
你看到rank了吗 用sql实现的 我准备都用sql

Dim:
用户健身质量评级?
个人的实时无所谓 全局的就抓下来 做个静态分析吧 好做文案,配文字什么的 接口留着,以后每年都可以用

FredericDT:
那我回去做个dump出来 然后咱研究研究大数据跟个人怎么结合

Dim:
这样,我回去画一份临时创意设计图给你,仅供参考,无论前后端都不必要按着这个做 (只是明确下大概的方向和成品大概要什么效果
然后我这边另外去做前端的技术准备 我给需要的单页和可视化库都给熟悉一下
顺便我再找导员沟通下品牌文案那边

FredericDT:
前端你准备怎么做

Dim:
emmm,我计划是 impress.js ,响应式支持桌面和移动 图表用百度的 ECharts 有问题再 fallback 到 d3.js ...
我不确定现在水平够不够,想做激进一点,练下我的前端,给我新个人页铺路
我会把涉及数据的地方HTML写工整点 我现在还不确定JS代码的具体分布,impress.js 试用一下尤其是移动端的情况再说
jQuery 需要吗?

FredericDT:
用吧

Dim:
好 我直接开工前端仓库了 顺便画图 顺便和导员沟通

FredericDT:
我一会儿先做功能文档和数据dump对吧
下面我就用telegram了

Dim:
可以,我最近tg都在线 加油

沟通后,导员的意思很明确:「希望成为针对每个用户的画像」,「尽可能导出所有应用的用户添加和使用信息,和普通的推送分别开,突出我们易班的技术性」,末了加一句「总之一定要有让人眼前一亮的感觉」。顺带介绍了组里的几位设计师支持。行吧。

搭建 Webpack 前端工程

要干活当然是先建一个 git repo —— 挖坑不填,不对,获得成就感的最快方式,自然是先煞有介事的把脚手架环境搭好。既然一开始就想好了是静态的长页面而非复杂的 SPA 单页面应用,所需的任务其实更是 gulp 和 grunt 等老牌前端自动化构建工具的长项。但是既然 Webpack 正好最近在接触,各种各样的插件也同样能胜任需求,不如就尝试一下吧。

这事一旦有了经验也不难,先 npm init 然后去最近的项目的 package.json 复制下依赖列表并按情况做个删减,然后补上 .babelrc 、 .eslintrc.jsonpostcss.config.js 和最重要的 webpack.config.js ,内容如下:

const path = require('path');
const url = require('url');
const process = require('process');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const publicPath = '';

module.exports = (options = {}) => ({
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: [
                    'css-loader',
                    'webpack-px-to-rem?min=3',
                    'postcss-loader'
                ]
            })
        },
        {
            test: /\.scss$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: [
                    'css-loader',
                    'webpack-px-to-rem?min=3',
                    'postcss-loader',
                    'sass-loader'
                ]
            })
        },
        {
            test: /(\.jsx|\.js)$/,
            use: [
                'babel-loader',
                'eslint-loader'
            ],
            exclude: /node_modules/
        },
        {
            test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 10000
                }
            }]
        }
        ]
    },
    plugins: [
        new CopyWebpackPlugin([{
            from: 'src/img',
            to: 'img'
        }]),
        new ImageminPlugin({
            test: /\.(jpe?g|png|gif|svg)$/i,
            // disable: process.env.NODE_ENV !== 'production',
            pngquant: {
                quality: '60-100'
            }
        }),
        new CleanWebpackPlugin(['dist']),
        new ExtractTextPlugin('bundle.css'),
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        })
    ],
    devServer: {
        host: '127.0.0.1',
        port: 8012,
        proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8080',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        },
        historyApiFallback: {
            index: url.parse(options.dev ? '/static/' : publicPath).pathname
        }
    },
    devtool: options.dev ? '#eval-source-map' : '#source-map'
});

当然,这份配置也是在后来的开发过程中不断修改的结果。最后打包的流程如下:

  • 对 JS ,入口 main.js 走 ESLint (代码格式检查)和 Babel (ECMAScript 实验特性兼容)打包到 bundle.js
  • 对 CSS ,全部使用不那么激进带花括号语法的 scss 格式后缀,先用 webpack-px-to-rem 插件直接转换 3px 以上的 px 单位到 rem 单位,再走 PostCSS 做其他处理,比如 autoprefixer ,最终利用 extract-text-webpack-plugin 导出到 bundle.css
  • 对 HTML ,直接用 html-webpack-plugin 导出
  • 对图片,小于一定尺寸的走 url-loader 编码成 Base64 ,其余利用 imagemin-webpack-plugin 压缩后用 copy-webpack-plugin 拷贝到目标位置,
  • 利用 clean-webpack-plugin 清空 dist 目录,然后重新生成

还为 package.json 添了两条 npm run 命令:

  • "dev": "webpack-dev-server --inline --hot --env.dev --host 0.0.0.0" 运行实时调试预览和 HMR 的开发服务器的 webpack-dev-server 插件,把 host 设为 0.0.0.0 而不是 127.0.0.1 便可以在内网的其他设备上方便的测试,比如用手机直接访问测试微信内置浏览器打开效果
  • "build": "rimraf dist && webpack -p --progress --hide-modules" 后来这里用 rimraf 取代 CleanWebpackPlugin 以便更好的支持不同平台下的删除权限

最终实际在生产时使用的库有:

  • normalize.css 浏览器一致重置
  • axios 异步请求
  • aos 跟随滚动出现和消失的动画
  • jarallax 视差背景图
  • echarts Baidu 开源图表库
  • echarts-wordcloud 文字云插件
  • clipboard-polyfill 全平台剪贴板控制

项目文件结构也很简单:

* src/
    * index.html
    * main.js
    * style/
        * ... .scss
* dist/
* static/

随意吐槽一下

一个是折腾 Webpack 配置文件的过程中,被人大力安利了高性能零配置的 Parcel 。看了几篇文章,感觉便利程度的确是很有吸引力 —— 但是看起来缺乏的自由度应该说明了其是个专注 Node.js 系快速打包的替代品,而不打算替代复杂的前端自动化构建工作(雪碧图等更多魔法操作)吧。

不得不提的是,如果项目中已经用到了 ESLint ,相比每次看 VSCode 「问题」面板或者 Webpack 编译时的报错然后手动不爽,完全可以安装 VSCode 的 ESLint 插件后,去设置里开起 "eslint.autoFixOnSave": true ,然后感受 cmd + S 后代码自己变得整整齐齐、报错全部不见的快感吧。

然后又说到 CSS 预处理器。想来因为和 BootStrap 的关系,初中我最早接触的还是 LESS —— 现在我也觉得 LESS 的官方文档最好读最舒服,设计也符合直觉。时隔多年重新整理自己的技术栈,读了不少对比 CSS 预处理器的文章,才知道 LESS 因为作者的短视和低扩展性已经过气,连 BS 官方也换成了 SASS 233 因此最近的几个项目都换成了 SASS 作为主役预处理器,不过又总是有人来和我安利 Stylus ,哦,和安利 Parcel 的是同一个人,好吧 …… 虽然明知道没有什么必然区别,哪个预处理器总之用顺手就好,不过下一个项目有机会还是试试看好了。

其实个人对 CSS 预处理器的利用程度也不算很深,主要是嵌套 CSS 的写法的确很爽,定义变量、适当 @mixin 、一些自带计算函数和表达式这种程度的特性也很有用,偶尔还需要 @include 一下方便组织 CSS 文件。回头多读读文档,看看还能不能发挥出预处理器的更多优势吧 ……

早期设计构想

导员很快拉了一个群,作为设计和文案的后援。

我:
总之,我们目前规划分为三大部分
「易班服务推荐」
「我的易班 2017 (个人数据分析)」
「易班校园大数据 2017 (校园数据分析)」
需要人员进行美工设计,和文案撰写
先问下你们有没有擅长网页/演示/PPT/展示/H5/平面/出版物/海报相关设计的 ... 参与到设计中来?
至于文案的话,估计是设计的留好格,再麻烦各位帮忙写
这样,我一会扔一份原型草稿上来,对自己有信心的同学认领一下视觉设计
用平面设计也就是海报的方法来做就好了

16-数媒-ZLX:
H5,平面,海报有点经验。。

17-数媒-ZJY:
举爪🙋🏻文案 H5 PPT 演示会好一点……logo什么的比较无力......

想着没有设计图,一遍遍的重复说明设计目标和任务总是很麻烦,于是,虽然对自己早已生疏的设计水准不太有信心,但是还是直接操起 Sketch ,尽可能的把脑海中想象的设计画出来了。

策划创意设计稿 第一版

策划创意设计稿 第一版

Dim:
先扔个图上来你们看下 ... 原谅我渣渣的视觉设计水准
不必照着这个设计,但是参考这个内容和布局组织,做一份更美化更具体的设计,并导出素材
唯一的要求是风格现代一点,我可以提供风格参考
emmm 有哪位同学觉得可以,来帮忙做视觉设计的吗

16-数媒-ZLX:
这个是最终应用于H5的吗?

Dim:
对 我负责前端代码
后期我会负责实现代码,以及加上一些简单的滚动动画,具体技术实现不用太担心,按平面的来走就好

16-数媒-ZLX:
带有拜年意味重要吗? 我感觉拜年挺难加进来的😂😂

导员:
对 挺好 可以加一些拜年的元素
没事 这种就在开头或者结尾加一点记一下 整体风格按照大气 科技感走

之后和后端的 FredericDT 同学沟通:

Dim:
草稿画完了 我的视觉设计水准差了点 = = 但是找不到好的视觉设计师
总之这稿你看着差不多?
我前端框架都起来了 设计搞定了就能动工(现在这不算搞定,还是太难看 ...

FredericDT:
我觉得可以有
“易班服务”是导员要求加的?

Dim:
行吧,我想想设计怎么改
不是啊,就是你说的功能推荐 我不知道你想怎么推荐 就那么搞了下 不然你觉得怎么搞比较好

FredericDT:
哦这样啊 这个要结合个人数据推荐的
“个人未涉及此功能才推荐” 个人数据摆在最前头

Dim:
那顺序是,个人数据,推荐,全校数据?
我改一下也不难,推荐直接作为个人数据最后的版块

那么好吧,蹭蹭蹭,改出第二版(其实只是修改了一下应用推荐的位置 ……):

策划创意设计稿 第二版

策划创意设计稿 第二版

Dim:
大体原型就这样了,原谅我渣渣的视觉设计和配色水准 ...
需要做一下细化的视觉设计,这份稿子仅供参考,只是说明下大概内容会有哪些,不必限制具体发挥
按 FredericDT 要求改了一版 ... 把应用推荐作为个人数据结尾的个性化推荐

初步的设计稿有了,前后端、设计外援、文案们也基本对最终的成品效果达成了共识,那么一方面等其他人的进度,一方面,前端这边也可以安心干活啦。

吐槽下初期设计稿的一些槽点吧。

因为我很明确的计划这次的项目想为自己的新个人页做设计上和技术上的铺路,所以倾斜容器 (CSS Transform Skew) 和视差滚动这些点是必须要有的。设计风格上,应该算作新个人页设计风格的衍生,所以设计前期的参考、灵感来源等等,会放到待新版本个人页写完之后的记录里一并叙述。

这里必须吹一把 Sketch —— 这两张简单的设计图我只花了不到一个小时就扒拉出来了,可以说 Mac 独占的 Sketch 这个优秀的原型和设计软件,是我必须一致选择 macOS 的主要理由和杀手级应用。相比全平台而且功能更强大的 Axure RP , Sketch 在同样的时间内原型画的更快、更好看,只要稍微花点时间就能上手大部分功能,常用的形状全部绑定到一个单字母快捷键,按 O 画圆按 R 画矩形, cmd + G 和 cmd + shift + G 快速控制组合,利用 option 和 command 键组合切换穿透选择、标尺对齐,还有 Scale 等比缩放,这些看似普通、 Axure (需要一个个拖拽控件)和 Photoshop (PS 里选中你想要的图层然后自由变换,不论性能和操作速度都差不少)的确也完全能做到的功能, Sketch 却做的更爽和更行云流水。几次在讨论设计方案的过程中随手用 Sketch 糊出一个原型,据说还给数媒院的学姐留下了「画图奇快」的印象。作为一个失职已久的自称设计师,接下来有空要做的,大概是更新一下机器上的 Sketch ,然后把大把 Sketch Workflow 的强大工具都再熟悉一下吧。

Sketch 大法好

Sketch 大法好

不过画的快是快,我自觉这俩张图还是画的很瞎眼的,因为是快速原型所以很懒,因为只需要做一张不怎么需要复用组件,所以 Sketch 源文件分组什么的都很不认真,配色都是随便拉的而且我出了名的不会配色,图表图片内容也是随手截图一个占位。本来是指望有靠谱的设计师帮忙后面能 ReDesign 补上这些,虽然后面看最后还是在自己这板稿子和数媒院同学们提供的稿子的基础上,最终在写前端时暴力发挥了 ……

同样,作为个人页的技术储备,我调查了一番现在前端静态页面比较常用的特效及其对应的库的实现,记录在知识库 前端实用库收集 – Dimpurr’s Knowledge Base #1 。在动工前本来想找出网易云那份教科书级别营销案例的个人听歌年报参考一下的,可以的话最好能直接读下页面源码,结果却发现已经找不到对应的链接了。思考过是用劫持滚动、类似网易云那样的分页设计(准备使用的是 pagePiling.js ,毕竟 impress.js 虽然酷炫,却更像是个前端版的 PPT 动画演示,而且太重了),还是普通的长页滚动,后来考虑到桌面端和移动端的尺寸和元素包括图表的布局适配,决定普通的滚动加上视差、滚动出现等动画即可。

设计稿里面用到一些图片做占位符,然而我没有本地积累图片素材的习惯 (真的需要的时候会去 http://unsplash.com/ 找免费授权的高清摄影图片,比如之前做的 BYR 日历壁纸) ,而我本地最大的图片库是看动画时的截图库 …… 这里面两张不露声色的占位符图片,有老司机看得出出处嘛 ……?一张出处应该是终物语,另一张是京吹。

另外你可能已经注意到的另一张占位符,是尾部的那张「谨贺新年」的新年贺图 —— 这是 一部我很喜欢的漫画 的贺图。这部漫画不仅在 Pixiv 上免费公开连载,并且作者慷慨的授权了汉化组在动漫之家上连载汉化,还于最近在中文圈人士的协力下推出了 官方中文电子版 。(那当然是买买买啦!)这位作者画风简练可爱,而且从东方同人漫画时期开始笔下的作品就有治愈有趣的剧情,值得安利一下。本来预想这处占位符用自制的新年贺图替换掉,但是意外的是却很受欢迎,并得到了导员的允许和作者本人的 Twitter 口头授权,因此一直保留到了最终发布的版本。(图片添加了 pixiv (c) nekotou 的标识)

小插曲:

hanyuwei:
这个结束的恭贺新年(
(钉子你知道来源么(

Henryzhao:
他申请授权了

hanyuwei:
我本意不是这个意思……

Dim:
@hanyuwei 知道来源
我还买了正版的中文版漫画第一卷电子版
还和妹妹一起看了 堂妹,没有亲妹

hanyuwei:
【捂脸】 天哪
你不怕你妹妹有什么想法……

Dim:
有什么想法 ...

hanyuwei:
【笑哭】 弄个一模一样的药

Dim:
才小六的幼女应该没这么聪明 ((

hanyuwei:
110

前端起步

不久之后,拿到了数媒院同学 ReDesign 的版本。虽然我口头说了不用太考虑前端,按以前做微信 H5 推送的思路来做就好,但是这设计稿实在有点 …… 前端不友善,不友善到几乎只能作为图片发布的程度。(虽然理论上怎样的设计稿前端切图工程师都应该有能力实现 ……)

ReDesign

ReDesign

不过没关系,虽然元素布局上大概不打算照着这份设计稿做(布局上我倒不认为比我那粗浅拙劣的初期设计稿好多少 ……),但是必须要感谢数媒院小姐姐出色的帮忙完成了我最懒得做的事情(233) —— 不知道从哪里找来了许多不错的标题背景等装饰素材。作为一个懒得切图的前端,了解过我过往作品的人都知道我多么喜欢用文字排版、简单易懂的 Div 色块和 CSS 特效代替图片装饰元素 ……

这次就算不情愿也得化身切图大师了,拿到图层整理清楚的 ReDesign 设计稿 PSD 源文件,先调整图像大小到原来的数倍确保清晰度, Alt 右键定位元素所在图层,再配合 Alt 点击眼睛隐藏其他所有图层,图像-裁切-根据透明像素,然后 Web 格式导出 png ,其他的交给Webpack 的 imagemin 插件自动压缩。

REM 布局

后端的 FredericDT 同学还在勤勤恳恳的跑 SQL 倒腾数据,那么不如先把前端能做的事情做了吧,搭个大体的页面框架,剩下中间内容空白好填图表数据什么的。这一步当然是设计 scss 文件结构,把页面基础、容器、布局、响应式什么的先跑起来,把个标题弄上去,要用到的各种库也 Webpack import 进去看看能不能用。各种上库、踩坑什么的倒没啥值得说的,值得一提的可能是 rem 布局吧。

前端早期工作区截图

前端早期工作区截图

其实早在 13 年也就是初二我就写过一篇 页面布局孰优孰劣:基于REM的EMs弹性布局——可用性的胜利 ,那时候 rem 布局还十分新鲜,几乎没有几篇国内资料,考据了不少英文资料才整理出这篇文章。现在一搜, rem 方案在移动端已经应用的十分广泛,主打的特点是「像素级实现设计稿」和「无损缩放」,各种教程也已经很多了。

在这次项目上,我倒是因为几年没碰前端布局技术有所生疏,犯了很大的原则性错误:一开始我设想了一个极其制杖的布局方案,就是在桌面设备使用响应式流式布局,然后切换到移动端后,再变成 rem 等比缩放;更制杖的是,我居然想靠  SASS 的 @include ,把桌面和移动端写进两完全独立的 CSS ,甚至还打算放进 @media 媒体查询里切换 …… 作为反省经验,我现在认为响应式断点布局还是一定要 Desktop First ,在桌面的基础上,移动端再做减法。至于响应式的流式布局,除非真的打算手机桌面端写成两套几乎无关的样式了,否则和 rem 固定非流式的等比缩放是很难也没必要共存的。

至于这次的项目我就干脆偷懒了,考虑到反正最后会看到页面的用户也就三四位数的量级外加大多会从手机打开 (我猜测主要的 User-Agent 是微信内置浏览器),所以和导员请示不是非得完美支持桌面端后,干脆一路 rem 缩放到填满屏幕,超过一定宽度之后直接露出黑边,页面实际内容缩在中间的长窄条。又嫌弃黑漆漆的背景感觉太没诚意,就弄了一套看起来有那么一点科幻酷炫的毫无意义的 CSS 滚动背景动画,似乎最后还挺受好评的就是了 ……

从实现上来说,我用的是 320px 为基础设计宽度(记得这是老款 iPhone 的分辨率标准,当初是觉得这世界上不存在比这更窄的手机屏幕了,后来发现这个尺寸写起来还是太碍手碍脚了,以后也许可以用个 640 之类的),然后 JavaScript 计算把 320px 拉伸到填满屏幕所需的 rem 并动态设置。一开始在 scss 里试了下手写 rem ,虽然只是 10px 的换算关系,但是还是别扭,于是去找了 webpack-px-to-rem 这个插件(同类插件很多,而且大多是国人做的,为什么呢?选了这个看起来 star 和 fork 比较多外加最近有维护的),之后就爽快的继续写 px 然后等自动转换啦。设计了最小转换尺寸大于 3px ,因为我习惯用 3px 做细微圆角。

因为我这里的设计是屏幕宽度大于一定尺寸就不再缩放占满宽度,而是居中两边留黑边,就用了一个比较简陋的 JavaScript REM  <html> 根元素 font-size 设置实现,原理很简单,保证屏幕宽度是 320px 的时候根元素字体大小是 10px ,其他等比:

const widthMobileBreak = 640;
var cwidth = document.documentElement.clientWidth;
document.documentElement.style.fontSize = (cwidth >= widthMobileBreak ? '20px' : (cwidth / 32 + 'px'));

必须说 rem 布局还真是爽,虽然不同平台字体不同,对布局来说还是个不可控因素,但是大体上可以在任何宽度的屏幕上有几乎一致的观感,而且由于这次页面中出现了「倾斜容器 (css tranform skew) ( + 视差滚动背景 (jarallax.js) + 模糊变暗 (css filter blur, brightness)」这样的组合,(尤其是 skew 对容器内 margin padding 和定位的影响很大,除非嵌套多层容器(但是又受到视差滚动的 js 库的限制),不然子元素会根据倾斜的文本基线排布,然后形状被 skew 回来),这个布局还是挺难控制的,用 rem 做等比缩放也比自适应方便了不知道多少(因为在边距控制上可以放心的写 magic number ……?(逃)。

BFG 删除 Git 污点提交

另一个不得不提的小插曲是,在切图过程中,我手贱保存了一份 ReDesign 设计稿 @10x 的 psb 文件,文件尺寸达到数百 MB ,而且一不小心 git commit 上去了 …… 发现这个问题时,已经 push & pull 了几回了,因为这个 git repo 是多地推送的,奇葩的是这么大的文件 USTCLUG 的 Gitlab 居然一下子就提交上去了,后来往 FredericDT 同学名下 Github 的 Private Repo 推送时,才因为异常的提交速度发现问题。这时候本地已经新增了很多有意义的 commit ,不想影响后面的正常 commit ,此时已经无法用常规手段抹掉这个文件了, .git 目录也大的不行。

按通行的搜索结果的办法跑了几遍 git filter-branch ,但是不知道为什么就是不行 …… 最后看到 如何清洗 Git Repo 代码仓库  这篇文章,这个叫 bfg-repo-cleaner 的 Java 小工具倒是意外的好用。一句  java -jar bfg-1.13.0-2.jar --strip-blobs-bigger-than 100M 就解决了我折腾半天的问题,之后 git reflog expire --expire=now --all && git gc --prune=now --aggressive 更新下再 git push --force 强推一把就可以了。当然,其中还遇到 Gitlab 默认禁止了强推,需要到网页端 Project Setting 关闭保护的问题。另外, bfg 这个工具的输出 LOG 有一段作者的私货,你能发现下图的亮点吗?

bfg-repo-cleaner

bfg-repo-cleaner

川普还真是不受待见,心疼 1s (

CSS @animation 加载动画

因为堆积了非常多的黑科技,当然也不得不担心一下在手机端性能下,以及在诡异的微信内置浏览器中的兼容性和效果。如前述,在 Webpack Dev Server 开起来允许内网访问后,直接用手机打开微信测试了一下。幸运的是,除了那些毫无意义的 CSS 和 JS 视差滚动动画多少不如电脑端流畅以外,并没有出现哪些让页面布局整个崩掉的问题,不管 CSS clac() 还是 flex 等都工作正常,各种特效也没问题。当然也许有一部分 postcss 的功劳,更复杂的兼容性情况也没有必要考虑,就这样了。

但是并不是没有问题了。一方面,头图等首屏图片素材的加载时间,让页面有一段很尴尬的显示不全的阶段;另一方面,尽管在 Chrome 显示正常,但是在微信内置浏览器中,为 REM 布局设置根元素 <html> 的 font-size 的 JS 需要过一段时间才会执行、而且 vh 和 vw 这俩单位的尺寸也会跳一次(???),导致页面布局也有一次闪动。因此,我干脆做了一个纯 CSS @animation 的加载动画,只用一个空 div (虽然挂在某个容器的 ::before 伪元素上也许可以不污染 HTML 结构,但是挂哪好像都不合适)写死时间,做了个很简单、效果也不错的全屏 loading ,底下会走一条没有实质含义进度条细线。

Dim:
别的没问题 加载动画死掉了 打开来一瞬间是这样
[图片]
然后闪了一下 然后又闪 ...
chrome 就一切正常 就很尬
我发现在微信内部刷新 也没问题 就是开新窗口有问题
解决了,就是 vw vh 这俩单位 在微信页面加载完成前表现不正常 直接去掉用 % 就没问题
就是体验有点糟糕,首先微信白色背景,然后我来个黑色背景的加载动画
算了,我的load也弄成白色吧 你有取色器么 取下这个颜色
[图片]

Heymind:
#ffffff 就是白的

Dim:
好吧 感觉黑色的加载比较有质感 但是为了不闪瞎眼 那就白吧
还行 内网ip这个是 webpack dev server 还有hmr的 2333

这种时候,SASS 这种 CSS 预处理器设置变量的好处就体现出来了。首先按一般合适的加载速度测算预估并写死了一个加载动画的时间,设为变量,作为 Loading 动画的持续时长、以及进入页面才开始的元素浮现的欢迎动画 Delay 延迟时长,多地使用。然后自己在开发测试过程中,本机加载资源并没有这么长的加载时间、每次等 Loading 动画也很妨碍调试,就利用 SASS 变量可以覆盖的特性写把 Loading 动画缩到极短,等开发结束部署上线的时候再注释掉。(也许结合 Webpack 和 Node.js 环境变量和 SASS Loader 参数,甚至可以自动判断生产环境,不用手动注释?不过懒得折腾了 ……)

CSS @animation 加载动画

CSS @animation 加载动画

一开始配合黑色的背景,写的加载动画也是黑底的。在 Chrome 中也效果不错,因为直接写进 <html style=""> 内联的黑色背景几乎会在页面加载的一开始就生效。后来发现微信内置浏览器在启动并加载页面前,无论如何都会看到不短的一段白底空页面,黑色的加载动画就显得闪烁太大,因此改为白底灰线的 Loading 动画了。

Loading 动画最终效果

Loading 动画最终效果

分析数据和设计图表

前端基础搞定后,就等着开始重头戏,分析数据、设计图表和文本内容了。找到后端的 FredericDT 同学:

Dim:
你这边情况如何 我前端现在等着塞东西了 等我 push 一下
BUPT_Shahe_Yiban_Year2017DataPresentation/design/dist-demo-180210/
这里可以看前端的效果
当然你可以自己 npm install; npm run dev 跑测试服务器实时编译,不过 npm 一般要搞半天

FredericDT:
那俩文档撸完了那仨还没动

Dim:
应用推荐那个你想好怎么做了吗 还有前后端怎么交叉
我的想法是这样的,个人数据不保密,不需要授权
搞个固定地址,比如 report.yiban.bupt.link/17/?uid=101010101 这样就是某个人的报告地址 这样底下还可以分享链接
但是易班应用本身进来有个授权 授权只是为了拿到 uid 然后跳转到这个固定页面
然后前后端分离的话,你直接撸出一大串 json 给我,我 axios 拿到了然后填进页面
后端在 report.yiban.bupt.link/17/api/?uid=101010101 ,随便你后端怎么实现,只要能根据这个 uid 输出合理的 json 就可以
我一会读一下你出的数据,然后设计一下这个接口,如何

FredericDT:
我比较意向于带有鉴权

Dim:
那这样不就没法实现「分享自己的报告地址给别人」
我感觉这个隐私程度不高,而且别人也不容易拿到你的uid
再说如果真有人暴力想爬,就让他爬来玩好了

FredericDT:
【遮眼睛】可以啊

Dim:
你后端对我黑盒,我不管了,前端这次也不用你来塞数据了
好,我现在去根据你的数据拟下合理的 json 格式
我们的数据是分「个人」和「全局」俩个吧,个人的要动态的
全校的应该静态写死到前端页面就可以了,关键要设计点有用的图什么的出来
倒是你没有认识什么擅长数据可视化的人来做这个吗 ...

FredericDT:
全校数据主要都是cluster

Dim:
你的进度如何,datas下面哪些是已经完成的
我应该读什么?哪些是产出 .py 我不用看吧 .json .csv 全都是产出?
Go to see '/root/dumpshell.sh' 这个是啥,我不看可以吗

FredericDT:
带readme的 各个应用下的readme是说明
'/root/dumpshell.sh' 这个是我的笔记,可以不看
读json和csv 不用看py
这些数据我最后还会再整合 就是再format 不会再做处理

Dim:
我现在读下 ECharts 文档看怎么用这玩意儿

FredericDT:
我把用户数据,全局数据都做成json文件

Dim:
没问题啊 完全没问题
但是用户有那么多,你全部做成静态json文件?

FredericDT:
其实没多少... 才一千个左右
主要看cluster怎么呈现
大数据主要做得就是cluster和sum

Dim:
我估计你的 json 部分 得直接考虑好 ECharts 这个 option 的事情
这个我看我俩得合作做了
流程是这样,我按照你的数据,根据文档设计一系列的图标模板,搞个一个 demo.html 给你
然后你根据 chartsdemo.html 里面我写的 option 根据后端实际情况调整,然后后端按这个 option 格式输出
没问题吧?

FredericDT:
没问题
你先去啃文档 我去把最后两个应用啃出来 1600见

做这段工作的时候,我已经春节回到了在乡下县城的老家,准备等着过年了。家里挤满的亲戚和孩子,真是嘈杂无比,但是所幸祖辈刚搬到了位于县城中央的现代化小区(虽然在这样的小区单元楼下大摆宴席和放鞭炮、在公寓单元楼之间放烟花也是一个奇景),我索性直接抱着 Surface 出来到附近商业区,四处寻找可以坐着工作的咖啡馆、奶茶店了。

老家外出时的 Twitter

老家外出时的 Twitter

不得不说, Surface Pro 还真是妥妥的生产力工具。自己沉重年迈并且因为电池寿命导致续航已经不堪重负的 MacBook 15′ with Retina Mid 2014 已经几乎变成了搬不出房间的台式机,平常外出就只好靠又能当平板看个书画个草图、消遣时能画个画打个 OSU! 、要写代码时键盘手感也非常赞的 Surface Pro 了,而且, Windows 下的开发环境是 iPad Pro 绝对取代不了的。

滋润的 Surface Pro 生活

滋润的 Surface Pro 生活

接下来要干的当然是啃 ECharts 的官方文档 了 —— 花了一个下午把「特性」和「教程」下的文章通读了一遍,意外的发现功能确实强大而且十分好上手,基本只要学会 option 项的写法、知道什么如何查 API 配置项文档,然后对照非常丰富的官方示例按自己的需求改就好了。这里必须吹一波 ECharts 这个难得良心好用的百度开源项目,性能极佳、官方文档详细并且提供了例如 表格数据转换工具 (可以方便的完成 csv 2 json ,记得在上一篇 BigDataGumi 一期小记 – 浅尝爬虫数据抓取和简单可视化分析 (PySpider)  文章里我不得不手写了 json2csv 的 Python 脚本吗?) 之类的实用小工具,而且可以用很少量的代码,实现扩展性和可自定义性丰富的、支持大量鼠标和触摸动态交互和自动动画的前端图表,支持 SVG 和 Canvas 两种各有优势的渲染模式。

在约定好的 16:00 和后端的 FredericDT 同学分头完成自己的任务后再交流进度,此时应该正式开始数据分析和图表的设计了。为了不过早的被页面具体布局和样式等不重要的问题打扰,我单开了一个 datadesign.html 并简单粗暴的 CSS / JS 全部内联,作为设计图表的草稿页面。

可以感受一下后端 FredericDT 同学的工作成果:

后端数据目录

后端数据目录

当时具体的协作过程,引用我事后吐槽的记录:

Dim:
FredericDT 也不容易,这些后端数据全都是他出的
xlsx 可能是他为了方便看吧,他大概是 sql 先 dump 出 csv ,然后 csv 转 json ,然后 python 洗个格式做个分析,然后还跑个 K-Means 聚类
有的全局数据直接 sql query 语句出,有的靠 python 数据工具计算
图表几乎全是我设计的,我读 FredericDT 出的数据 json / csv 和 readme 说明,然后找 echarts 可用的图表类型, surface 画设计草图,写个需要的数据格式 mock.json 给他,然后我用 mock 数据写图表,他照着要求再洗一遍数据按要求转格式,我再在最终页面 getJSON 填进真数据 ...
流程就基本是这样,设计图表这部分也就花了两天,不过那两天一天工作时间最少5h+,所以设计图表花了我最最起码10h+ ...

在 Telegram 上和 FredericDT 同学一边讨论已有导出的数据,我一边在官网示例物色合适的图表类型, FredericDT 同学同步在本地用 pandas 和 matplotlib 倒腾数据和预览图表,两人一边吐槽一边尽可能在草稿页上完成最终的图表,整个流程就是这样的了。

怎么说我也是在之前 BigDataGumi 的数据可视化项目里做过无脑统计分析图的,还觉得自己算是有些许经验了,没想到,这次我随手挑选的第一个应用,就用现实给了我一个猝不及防的打击:这个 shahe_check 应用数据量比自己想象的还要小 —— 只有 3 次活动的使用记录 ……

但是数据小也得装成很多啊, 3 个活动,总共 300 人的签到时间戳,外加 FredericDT 同学跑出来的聚类数据,几个签到高峰点的时间戳和权值大小。怎么把只有一维(时间轴坐标)的数据呈现出来并且显得很厉害的样子?答案是暴力增加维度,把时间粒度压缩,按每 5% 时间比例统计人数,那么 300 人就变成 20*15 尺寸的折线图 …… 拿出 Surface Pen 快速的涂一个设计草稿,说明要求后,就麻烦 FredericDT 整理数据导出 json 了。

当时情况大概是这样的:

shahe_check 应用 Surface 草图

shahe_check 应用 Surface 草图 (字丑求忽略 Orz)

Dim:
我在做 shahe_check ,现在的想法是出一个签到人数随时间变化的曲线图 你看这个怎么样
[ Photo ]
反正就3个 events ,我也不知道还能怎么玩了
还有个 -1 咕咕咕,我不知道怎么展示比较好
主要是3个events太少了 怎么展示咕咕咕比例,又不突出我们只有寒酸的3场events

FredericDT:
这个可以有 我设计的score_function就是干这个用的
[In reply to Dimpurr Cheny] 不要展示这个..... 太羞耻了 推广不足

Dim:
那就过滤掉咕咕咕
感觉折线图的话数据不够 我想想能不能做成散点
每个事件大概 120 条 check 打 300 个点肯定好看,问题是我们只有一个维度,就是 score 必须暴力造多一个维度
如果按 score 合并,比如 0.2 的全部合并,作为计数,就有 y 轴了

FredericDT:
这样 我把 cluster 质心数调高 用cluster造segment
这是把三个活动都统计过后聚的签到peak 我把每个活动都跑一遍k-means 然后count label作为y x就是centers

Dim:
怎么可视化cluster 这样的话画散点?还是折线?

FredericDT:
折线
质心数大概多少合适?

Dim:
考虑手机的屏幕宽度 其实还好 7 个左右? 现在已经是针对移动端设计了
{ eventid: 16, type: 'line', stack: '人数', data: [ ... x7 ] }
这样的搞的定不 不一定用中文,人数可以 "count" ,随便你喜欢
你最后出个数组给我,我就直接写 serires: json.data 了

FredericDT:
我先找个活动做个7个质心的

Dim:
我思考下 我觉得你画 cluster 还是数据太少了 画来画去就那几个点
要不还是尽可能把每一个 check 记录都画出来 然后 cluter 作为图上的额外标注
你这样行不行,拿两位小数做粒度
横轴 score 纵轴人数,按 0.05 为粒度,画 3 条 check 热度的曲线,标注3次事件名称和注释
然后 cluster 的话 作为竖线标记进去 我看看怎么做额外标记
markLine 有个这个
你就想办法按 0.05 为粒度计数给我成不 按 100 个人计算 期望值在 5 左右
勉勉强强啦 20 * 5 的图,毕竟只有 100 个人 ...

FredericDT:
我撸个segment函数出来 然后跑个结果

Dim:
我现在格式给你
{
    "data": [{
        "eventsId": 16,
        "name": "事件名称1",
        "description": "事件",
        "count": [
            [0.00, 3],
            [0.05, 10],
            [0.10, 3]
        ]
    }, ...
}
markline 我直接定死两条,加上颜色视觉区分,早中晚
我現在在push 效果還行
[ Photo ]

FredericDT:
pushed 可以

Dim:
这个 app 结束吧 下一个来哪个
你手头有没有什么绘图软件,或者干嘛的 你也设计几发图表吧

FredericDT:
pyplot
吃饭! 吃完继续

Dim:
emmm,也好 我也得换个地方吃饭

当时内心草泥马滚滚而过的我还忍不住发了条 Twitter 吐槽:「凄惨的现实让我难以正确的进行小数据可视化」……

Telegram: 凄惨的现实让我难以正确的进行小数据可视化

Telegram: 凄惨的现实让我难以正确的进行小数据可视化

好吧 …… 当然,现实也只能接受,之后其他应用无非就是如法炮制了。比如 gym 应用是这个画风:

gym 应用 Surface 草图

gym 应用 Surface 草图

过程大概是这个画风:

Dim:
bupt_id,attend_time_1,attend_time_2,attend_time_3,gugugu_time_1,gugugu_time_2,gugugu_time_3,score_rank
bupt_id,count_of_successfully_attened_time_1,..,..,count_of_gugugued_time_1,..,..,score_generated_by_a_fomula
这个 1 2 3 是什么鬼

FredericDT:
健身的三个时段 对应18-19,19-20,20-21

Dim:
personal-gym-text
    if data.count>0
    `你在过去一年里预约了 ${data.count}(3) 次健身记录,最喜欢的健身时间段是 ${data.favTime}(19:00 ~ 21:00) ,在全校的排名是 ${data.rank)(1...144)
    你获得了称号:
        1-3 北邮健身王
        4-40 身强体壮
        50-100 健康达人
        100-144 有点慵懒
设计了一段文本
扔给文案组去改写文案了

FredericDT:
这个扔用得好
【捂眼睛】
我把csv转成json?

Dim:
我们到时候 json 太多了不好吧 尽可能全部数据合并到一个 json 不然请求数太多
这个数据有啥用你觉得

FredericDT:
我有一个健身指数function
显示个 总计、履约率、健身指数、健身指数排名
健身指数是个int
我给其整体加个bias
原函数有负值
sigmoid * 1000 吧

Dim:
gym 怎么做全局分析
我感觉根本不存在全校大数据
apply_distribution.json 这个存在3个脏数据
而且太接近了,几乎就每天都是满的
这数据没啥意思 ...

FredericDT:
嗯 出个sum吧

Dim:
累计预约多少次是吧
你方不方便看一下每个时间段 就是每天 1 2 3 时段预约人数 如果有波动的话可以画个3折线

FredericDT:
每天各时段饱和

Dim:
那就没意思了 那就出一段文本?

FredericDT:
出个平均赴约率吧 还有平均预约次数 文本呈现

Dim:
全校学生过去一年里预约了 ${data.total_count}(12443) 次健身记录,平均每人预约 ${data.avaerge_count}(3) 次,并在 ${data.avaerge_attend_rate}(0.6)*10 % 的预约中准时赴约
我把文本内容移到 datadesign/readme.md 了 datadesign.html 专门用于图表

FredericDT:
gym/gym_personal_report/gym_personal_report_total.json

Dim:
ok 平均预约次数呢 有办法统计用户数量吗
要不要给个人的 gym 记录做一个 calendar 图
不过 ... 实在太少了
能不能合并下 gym, check 之类的数据 合并成「该用户使用易班服务的次数」之类的
仿 github 连击combo图 ...

FredericDT:
等等..数据有毒 我重做一个

Dim:
我弄了个 g.json 直接填真实数据进去 你搞定了就直接手写进去就好了

中间还碰到一个应用,数据量小到让我们最终不得不放弃分析,此时我们的内心是崩溃的 ……

Dim:
快过年了 ... 剩下俩也搞定了吗

FredericDT:
易物... 😱 我都不知道怎么整 剩下俩还没写文档
易物太惨了 总计14条数据

Dim:
那还玩个毛线啊 ... 导员还说「要表现出大数据」
哈哈哈哈哈哈
给我下进度 ... 总共多少个 app / 已经搞定多少个 app
那我就等你写完文档再看呗 ... 那就是进度 3/5 呗

FredericDT:
3/5/6 文档/处理/全
易物我不打算做大数据了 太坑

Dim:
累死我了,我喘口气然后看看 borrow_room
文本信息多不多 能不能做标签云

FredericDT:
给你看一下易物

FredericDT:
swatch
计算机学院本科课本
一辆自行车
一双穿了20分钟的足球鞋
一个完好无损只用过一次的桶
中兴投影仪spro2
刚买的自行车,实心胎,仿死飞
自行车 能骑就行
大号桌斗
丽芝士纳宝帝奶酪味威化饼干
丽芝士雅嘉奶酪味玉米棒
玛丽黛佳酷黑眼线液笔
一个超级好用的3ce隔离粉底
小米6全屏贴膜

FredericDT:
😱 我好方

Dim:
还有吗 数据量就这么一点?
我觉得你直接不管三七二十一 把这堆东西做成文字云
尺寸我直接 math.random 你一个数组把全部交易过的物品 (超过 20 个我觉得就够了) 给列出来

FredericDT:
就...这些
一共就这些...

Dim:
... 还有啥信息吗,价格之类的 这是以物易物还是二手平台

FredericDT:
有价格 二手平台

Dim:
1. 画一张散点图,横轴时间纵轴价格,点上标名称 2. 按价格设定大小,做个文字云
你看 1 还是 2
剧毒 少过头了一点吧 ... 除了标题,还有 desciption 吗 有 desc 的话可以从里面截词

FredericDT:
有 很不正经 全新 是高频词
看来只能这样凑合了..

说到文字云,用的是 echarts-wordcloud 这个插件。由于每一次的文字云都是动态生成,所以每一次刷新的文字云的布局和内容位置都不一样,也是十分有趣。

最后成功运用了两处,一次是 global.borrow_shahe.wordcloud 中的场地租借理由, FredericDT 同学在 Python 上跑 Jieba 分词并统计词频后,导出高频词权值对格式的 JSON 文档,生成场地使用理由的关键词云。另一次是 global.askforleave.wordcloud 中的请假回家理由,这个如果跑词频想必会是同质化很高的「回家」霸权,于是我干脆直接利用 一个在线标签云工具 不支持中文的特点,正好达到了以标点符号为分界、整句生成标签云的效果,然后拿在线生成结果的 SVG 代码扔进 VSCode 正则表达式替换,直接变成了我所需的 JSON 格式。这其中也是看到了不少充满个性的请假理由,我个人觉得「北京孩子要回家!」、「每月去看女朋友一次」这两条算是十分逗的了 ……

askforleave.csv 的请假理由

askforleave.csv 的请假理由

其他的应用处理过程和槽点大概雷同,也就不再贴记录了。最后,折腾好的 datadesign.html 大概长这样:

datadesign.html

datadesign.html

最后折腾出 ECharts 图表实际上也就这么 5 个,至于非图表而是文字数据展示的部分,则被直接写进了 datadesign/README.md 和文案用文档里。

文案部分

在 FredericDT 同学按要求折腾数据和导出 json 时,另一边我也在一方面对着文档调出符合要求的 ECharts 图表、修改 option 。在写代码时有点难以转换脑子再去承担文案工作,于是在最终成功合并数据、能够预览图表效果了之后,我设计了一个石墨文档,以便把出好的数据图表截图和随便写的文案实例交给文案协力同学们,拜托他们撰写引导语、详细斟酌说明文字。

并没有太多值得说的地方。应该可以看出来这些文案文本最后大多是用 ES6 的「模板字符串」形式,拿到 JSON 数据后异步填进了页面。另外就是在线文档协作大法好。你可以直接围观文档的副本:https://shimo.im/docs/JQIGIG7D8Nc7sx3n/ 「易班H5PRE年报提纲 公开副本」

易班H5PRE年报提纲 文案用石墨协作文档

易班H5PRE年报提纲 文案用石墨协作文档

后来文案组的同学这么吐槽:

Dim:
@17-数媒-ZJY 现在基本上数据和图表都出来了
问题是,怎么把这些东西有机的塞进一个页面里
麻烦你组织下先后顺序,承接文字,每个图表的文字,之类的 ... 然后我就按照这个放进页面
样式设计我参照之前的设计图自己发挥 差不多就好了

17-数媒-ZJY:
好的我尽量
出了的都会发到之前那个网址吧
【感觉自己像在写高考语文图表题 👀

数据和图表处理阶段,其他的槽点

直接贴一个我事后和别人吐槽的聊天记录吧:

Dim:
行了行了,收工了,这个鬼年报搞了我好久啊
可能是我近来最大的工程

Heymind:
emmm 真·好久
【盯】 你也不偷个懒。。。

Dim:
233 其实你不觉得这玩意儿 整个微信公众号推送文章就完了么
图表也是, FredericDT 他 python mathplot 都能画,我还得 ECharts 画一遍
至于个人报告,也就那么两段,其他都是全校的 ...
至于视差/倾斜/各种高级 webpack 玩法,全都是我在瞎折腾 ...

Heymind:
我是觉得。。。。可以变得轻松。。。云端生成图表啊。。。

Dim:
2333 Echarts 主要是可以鼠标交互,比较酷炫啊,还支持触摸的,手机效果不错

Heymind:
还能交互?
还真能交互

Dim:
你居然觉得那个图表是张图?!
尤其是那个折线,沙河签到的 好玩死了,下面还能选数据范围 鼠标滚轮可以缩放

Heymind:
emmm 是的 我才发现底下的框框能拖动
2333 你不标注说明一下谁知道。。。

Dim:
对啊,可以整体拖动和两端拖动
2333 就连文字云都能 hover 呢 ... 文字云也是 ECharts

Heymind:
假大数据又一案例。。。 emmm 这数据到1k+了么
可视化没劲。。。 跑数据挖掘啊。。。

Dim:
gym 是数据量最大的 但是我们也就生成了一段话而已
至于,只有三个活动的签到系统之类的 完全是在打肿脸充胖子
那两个文字云也是,实在 TMD 没有数据可分析啊,只好从文字理由里弄出点数据
请假理由我连分词都没法分了,直接整句生成文字云
整句读起来还比较好笑,要是做个分词基本就一个词「回家」 ...
2333 而且 FredericDT 也是很有毒 他不管什么数据都要跑一个 K-Means 算 Cluster
最后用上只有 请销假那个不明不白,我都看不懂的热点图
其实想法很好 非要分析可以分析出每一个点对应的事件 比如最大那个泡泡应该是 枫叶红 啥歌赛 的 但是写文案的人表示无能为力 ...

Heymind:
2333 这个需要跑聚类?

Dim:
我是觉得大部分跑了也没啥意义,但是 FredericDT 喜欢跑,几乎每个应用都跑了 ...
【前方引用过部分,省略】

Heymind:
我猜他应该大部分时间是在解决祖传代码233
图表设计是。。。这个echarts定制性那么强? 我以为就是填数据出图。。。

Dim:
就是根据 FredericDT 出的数据文件设计那些 echarts 图表啊
强的要死 你只要做一系列设置 其他全都自动化,自动计算动画,自动自适应
你读一下 charts.js 就知道了 setOption 就是每个图表的核心代码

Heymind:
这些应用如果全是微信小程序。。。估计全得上一个量级

Dim:
微信小程序复杂度那么高?

Heymind:
我说用户量级。。。 // 黑的好

收尾阶段

贴几张工作中期截图:

工作中期截图 1

工作中期截图 1

工作中期截图 2

工作中期截图 2

工作中期截图 3

工作中期截图 3

攻克了其前端所有的新技术难关,切图导出了所需的素材图片,分析数据设计出了图表和文案,几乎最难的公关部分都已经完成了,那么剩下的只是把 datadesign.html 里的 ECharts 图表们、文案文档里的文案们,配上导出好的 JSON 数据,填进页面里并处理好细节样式了。

最后的前端处理,就是不断的修改样式,同时截图发到群里麻烦设计师同学和朋友们提供建议,然后继续按照建议调整细节。似乎也没有什么太多值得一提的点,就随意吐槽一波吧。

一个值得一提的建议来自一位做混音的朋友,一开始用 normal 字重、 bold 强调的页面看起来视觉上太过浑浊,于是她提出可以用 lighter 作为全局基础字重、对标题和数据数字采用 normal 字重做强调,效果意外的好。(毕竟现在大部分平台应该都有苹方 Light 、雅黑 Light 这样的中文细体支持了吧?)另外还记得最近有看到过 CSS font-weight 属性用数字替代关键字会更好的说法,但是一时没考据到出处,就没有纠结下去。

原本设计用于注音的 HTML <ruby> 标签(可不是编程语言的那个哦)可以方便的做出文字顶部标注的效果,被我用来在底部的署名上标注了 dimpurr 的 ID ,并加了个难以察觉的链接 233 上线后,发现 FredericDT 同学也发现了这点,并且自己加上了自己主页的链接 (

开发过程中经常需要生成页面的长截图,当然 Chrome Dev Tools 里内置的 Ctrl+P 命令面板的 Capture full size screenshot 命令往往是第一选择。但是这个功能的原理似乎是强制生成一个缩放比例巨大的视图,并在一屏幕里完成截图(纯属猜测),因此会导致页面中根据视图尺寸控制背景图片大小的视差滚动插件显示不正常。后来用的是 Chrome Store 里找的 FireShot 插件,用的是经典的自动滚动连续截图+拼接长图片的截图原理。

MacBook 充沛的性能、优先保证顺畅反应的 UI ,还有 macOS 的的多指手势、多桌面和窗口管理、「调度中心 (Mission Control)」视图、 Spotlight 等等特性,对于开发效率的帮助真是太大了。因此在 Mac 下养成了忙碌工作时窗口只开不关、后台程序也毫无节制以至于要用 Bartender 隐藏长长的 icon 列表、搜索时动不动打开数十上百个 Safari 标签页同时加载、找不到了就双指一捏显示所有标签页等等坏习惯之后,刚入手 Surface 时,甚至还真一时半会有点难以适应 Windows 憋屈原始的手势和窗口交互、不时卡顿的性能 ……

macOS Mission Control

macOS Mission Control

(干活时一不小心窗口就会只开不关,毕竟基本不影响性能、而且不知道啥时候又会用到,而且窗口再多、需要时也能马上定位到目标窗口,等手头工作结束后再 CMD + Q 慢慢清理窗口。)

最后来看一下完成的效果吧

yiban-report-2017-fin-r1

yiban-report-2017-fin-r1

主体内容收工后, Webpack 编译一遍并把 dist 目录交给后端的 FredericDT 同学(跑一轮 Imagemin 需要的时间还不短),把链接之类的内容从占位符替换成实际的应用地址,测试上线、在 Chrome Dev Tools 的 Network 面板观察请求数和体积,并在 WeChat 内置浏览器这个最重要的目标 User-Agent Client 上观察效果。还不错, index.html, bundle.css, bundle.js 几个主体文件加上数据 json 加起来不到 500K ,手动处理了几个较大素材图片之后再过一遍 Imagemin 后,完整体积从 15MB 降低到了 4MB 。应该没问题了。

稍微备注一点,这个页面目前只能从校内网访问,所以上线地址就不用在校外渠道和我要了。

结束

感谢你看完了这篇又冗长又没有重点、主要的意义在于流水账一般记录项目的整个过程和漫无目的的吐槽的文章。

每经过一个新的项目,都会学到很多新的知识和经验。是这种经验在我看来,要么是「去看书看官方文档啊」「去 Google 或者 StackOverflow 啊」这种程度、没有必要重复一遍的知识,要么是会被我简单粗暴塞进 知识库 的归纳整理, 总而言之,总感觉只要是自己能通过搜索引擎解决的、也就是已经被解决的问题,除非为了刻意吸引搜索流量,否则没有什么转化为博客文章的价值。久而久之,感觉博客就成了纯粹发布作品和记录生活的地方了。所以,这篇文章提供的也不是技术价值:既不是什么前端工程的最佳实践的反思,也不是什么新的设计模式,或者其他终归要看书和看文档得到的知识。这只是一个比较有代表性的小项目的全程实录流水账,应该说,更多的是记录项目过程中本身的槽点和乐趣。

如果你对这篇文章所涉及的领域还比较陌生,我希望这个第一视角的体验能让你感受到,一个完整的前端单页面项目是怎样被设计、策划、开发出来的,中间会涉及到哪些技术、工具和流程,以及最重要的,体会到和一群有趣的人合作做一个小作品的乐趣。当然读者的你更有可能水平在我之上,那么还烦请针对我暴露出来的不足不吝赐教。

这篇文章也提出了几个有意思的 TODO ,那么接下来我该做的,大概是再深入了解一下 CSS 预处理器们有用方便的特性,还有 Sketch 强大的周边 Workflow 吧。从高中毕业开始至今一个学期的前端复健似乎还算顺利, Vue 也已经了解的七七八八了,当然, React 和 Angular 的补习也不能落下。

感谢项目全程提供鼓励和支持的辅导员,感谢曾经参与和协助开发并提供建议、以及授权这篇文章使用与你们的私人聊天记录的 FredericDTHeymind、ZJY、ZLX、LWJ同学和绘露群的各位,感谢授权使用插画的画师 @nekotou ,以及感谢 CCTV 或者其他更多的什么东西。

项目开发和文章撰写过程中全程循环 BGM 「DAOKO – Forever Friends」与「Sarah Brightman  – Scarborough Fair」。

  1. Dimpurr说道:

    其实写这个文章我有个目的是,以后要是有萌新好奇前端开发、项目协作的具体流程或者大学里的项目是什么感觉之类的,我就把这篇文章扔过去给他看 233 不过写完一看还是内容结构和重点主次有点混乱,主要是写完太累了有点懒得润色了 Orz 这次就这样了吧 (

  2. printempw说道:

    真好啊真好啊(来自苦逼高四选手的怨念)

    另外 CSS 预处理器我比较喜欢 Stylus,因为所有特性都是可选的(我对那种魔改到完全不像 CSS 的预处理器不怎么感冒)

    1. Dimpurr说道:

      嗯,想写普通的 CSS 也完全可以,需要的时候又可以引用额外的预处理器特性,这样很爽 (
      高四加油!

      1. printempw说道:

        谢谢 XD

  3. Messiah说道:

    为什么这么强啊

  4. ghost说道:

    看结语差点哭出来…因为自己所知道的都已经有各种资料了,就再也没有写过技术向博客,到后来连博客都不怎么写了。读着很有日常感的第一视角,感受到了另类的被碾压的快感,伪宅突然想跳坑技术宅了orz

    1. Dimpurr说道:

      好呀好呀,技术宅有意思啊 233 其实所有创造性的工作都很有意思。

      写这篇文章我就是想传递一下这种有趣的日常的感觉,也算回应一下我高中对大学生活的期待。看反响效果还不错w

  5. Alchemy说道:

    大佬真强,手下膝盖

  6. Chang说道:

    没有接触过前端的我表示读了两遍这篇博文,然后上你的github star里挑了挑例如“前端学习指南”的repo,然后百度了一下一些名词比如 EChart 之类,大概对前端的工作模式以及怎么和后端、美工合作有个了解了。写的真棒,真是一个充实的寒假呢。(灵梦和魔理沙好可爱哈哈)

  7. loney说道:

    收下膝盖

  8. lwl12说道:

    好像还是首次看到这种类型的文章……hummm虽然读下来确实比较困难但是也cover了一些平时文章不会讲到的设计/协作之类的细节……
    总之还是钉子太苣了(

  9. Rabbit说道:

    大佬 膝盖是你的了!

Chang进行回复 取消回复