《你的性格主导色》揭秘
介绍
《你的性格主导色》是今年网易云音乐前端团队开发的一款测试用户主导色的 H5 应用,上线后反响很好,刷爆了微博和朋友圈。
项目的主要开发者 imyzf
发表了一篇文章《官方揭秘!你的颜色是这样算出来的》,解释了一些动效和最后主导色的计算方面的问题。但由于涉及到了具体的业务,所以作者没有开源出源码,但是热心的作者给了很多的提示。我就是根据这些提示,揭秘了我比较感兴趣的部分。
在线 Demo
由于一直没有在生产环境中使用Vue3.0
和vite
,所以源码部分我使用了 Vue3.0
+vite
实现。
页面预加载
答题类页面与一般的 H5 页面的不同之处在于,用户的操作路径是确定的,即每个页面的下一页路由是固定的,所以在 router 层面做了优化,提前预加载了下一个页面由于活动页面使用了大量的视频和动效等,所以想在用户阅读选择题目的过程中把下一页的页面渲染完毕,这样切换到下一页面的时候会很流畅,体验很好。
最初就想着怎么利用
vue-router
完成页面的预加载。但是搞了一圈发现,都是基于webpack
或者vite
的懒加载,提前加载了一些资源,并不会提前渲染出页面。后来通过看
vue-router
文档,才找到了灵感,利用命名视图,同时展示 2 个视图,使用css
隐藏下一页,这时候虽然不显示,但是页面已经渲染出来了。通过修改
router-view
的 name
属性,完成页面的切换。也就是说,其实我的路由是没有变化的。// App.vue
// 注意 ,这里使用两个 viewName 完成了页面的跳转,next 的页面被预加载
const currentViewName = computed(() => store.getters.currentViewName);
const nextViewName = computed(() => store.getters.nextViewName);
// router的定义部分
const routes = [
{
path: '/',
components: {
default: Index1,
index2: Index2,
session1: Session1,
session2: Session2,
session5: Session5
}
}
];
看上面的代码,
Index1
、Index2
和Session1
等其实就是每一页的组件了,通过修改currentViewName
和nextViewName
就可以达到页面切换的目的。最终的效果是下图这样的,下一页已经提前渲染出来:
文章图片
翻页动效 作者提示说使用
canvas
实现了页面切换时候的幕布拉动效果,主要运用了最核心的 canvas API
是 bezierCurveTo
。通过查询得知,
bezierCurveTo
需要 3 个 点用来绘制三次贝赛尔曲线,在线体验看下图,想要实现拉动动画,
P1 P2 P3
的X
轴坐标需要持续变化,然后绘制曲线,就能够实现拉动的效果了。文章图片
我这里使用了比较轻量的
JavaScript
动画库animejs
,用来控制上面几个点的持续移动。3 个动画效果分别移动了P1 P2 P3
的X
轴坐标 ,再配合曲线的绘制,就达到了基本的拉动幕布效果。const heights = [0, 0.5 * pageHeight, pageHeight];
points = {
p1: {
x: pageWidth,
y: heights[0]
},
p2: {
x: pageWidth,
y: heights[1]
},
p3: {
x: pageWidth,
y: heights[2]
},
p4: {
x: pageWidth,
y: heights[2]
},
p5: {
x: pageWidth,
y: heights[0]
}
};
// P1点的变化
anime({
targets: points.p1,
x: 0,
easing: 'easeInQuart',
delay: 50,
duration: 500
});
// P2点的变化
anime({
targets: points.p2,
x: 0,
easing: 'easeInSine',
duration: 500
});
anime({
targets: points.p2,
y: 0.6 * pageHeight,
easing: 'easeInSine',
duration: 500
});
// P3点的变化
anime({
targets: points.p3,
x: 0,
easing: 'easeInQuart',
delay: 50,
duration: 500
});
// 画曲线
anime({
duration: 550,
update: function () {
// 清除上一次的绘制
ctx.clearRect(0, 0, pageWidth, pageHeight);
ctx.beginPath();
ctx.moveTo(points.p1.x, points.p1.y);
// 幕布的上半区域
ctx.bezierCurveTo(
points.p1.x,
points.p1.y,
points.p2.x,
points.p2.y - 0.2 * pageHeight,
points.p2.x,
points.p2.y
);
// 幕布的下半区域
ctx.bezierCurveTo(
points.p2.x,
points.p2.y + 0.2 * pageHeight,
points.p3.x,
points.p3.y,
points.p3.x,
points.p3.y
);
// 已拉动部分的矩形区域
ctx.lineTo(points.p4.x, points.p4.y);
ctx.lineTo(points.p5.x, points.p5.y);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#f1f1f1';
ctx.stroke();
}
});
最终完成的效果是这样的:
这个动效由于每一页都需要使用,所以考虑完成一个通用的全局组件。
考虑到使用的时候一般组件需要写到
vue
模板上面,很不方便,所以最好通过一个全局函数直接显示这段动效,类似于showAnimation()
;
首先需要完成一个独立的组件,由于想覆盖掉页面的所有信息,所以使用了
Vue3.0
最新提供的teleport
组件:
然后需要把组件通过 Vue 插件的方式注册到全局属性,由于我想使用
Composition API
,所以最终决定使用 provide
+ inject
的方式注册和使用全局 property
。一般的情况下使用app.config.globalProperties
就可以了,但是这种配合Composition API
写起来会比较麻烦,不推荐。(Mask as any).install = (app: App): void => {
// Vue3 的 Composition API 建议使用 provide + inject 的方式注册和使用全局 property
app.provide('mask', Mask);
};
// 使用的时候
const Mask = inject('mask');
最后,由于翻页动效和路由都在一起使用,就继续封装了个
useNext
函数,这样在一般的view
组件使用的话,就非常简单了,同时做了翻页动效和翻页的操作:nextPage();
到这里我可以夸夸
Composition API
了,非常的简单和方便,通过这个全局通用组件的封装,我彻底喜欢上了这种方式。云层动效 这部分是我觉得最有趣的,以前用
three.js
实现过一个 3D 照片墙,但是这个云层动效真的牛,也是最难破解的,还好被我搞定了,下篇详细说明破解的过程。源码 【《你的性格主导色》揭秘】最后放上源码,感兴趣的同学可以看一下,欢迎 Star 和提出建议。
推荐阅读
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 《跨界歌手》:亲情永远比爱情更有泪点
- 诗歌:|诗歌: 《让我们举起世界杯,干了!》
- 期刊|期刊 | 国内核心期刊之(北大核心)
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- 人间词话的智慧
- 《一代诗人》37期,生活,江南j,拨动心潭的一泓秋水
- 广角叙述|广角叙述 展众生群像——试析鲁迅《示众》的展示艺术
- 书评——《小行星》