滴滴首页动画实现
首先我们得看下具体长啥样
这个不是有手就行?
- 首屏的车流滚动,经过分析,并不是一个
video
标签,此处应该做了判断,pc端用video,移动端使用图片的解决方案,
其实到这里大概就清楚了,DOM能很清晰的反应这段代码的逻辑。
so,说干就干。
首先你得拥有一段视频
自己准备
视频如何处理?
选择的工具:nodejs
,ffmpeg
,child_process
- 如果视频过长,可以选择先把视频截取自己需要的部分
// 视频截取
const { spawn } = require('child_process')
const videoPath = './video/hot-girl.mp4'
const outPath = './out'
const segmentTime = 60 // 视频时长
// 切割视频
const ffmpeg = spawn('ffmpeg', [
'-i', videoPath,
'-c', 'copy',
'-map', '0',
'-f', 'segment',
'-segment_time', segmentTime,
`${outPath}/out%03d.mp4`
]);
ffmpeg.on('close', (code) => {
console.log(`child process exited with code ${code}`)
})
ffmpeg.stderr.on('data', (data) => {
console.log(data.toString())
})
- 把视频按帧截图,或者按时间截图
const picVideoPath = './out/out011.mp4'
const picOutPath = './out/pic'
const ffmpegArgs = [
'-i', picVideoPath,
'-vf', 'thumbnail',
'-frames:v', '1',
'-c:v', 'png',
'-f', 'image2pipe',
'pipe:1',
`${picOutPath}/girl%d.png`
];
const ffmpeg = spawn('ffmpeg', ffmpegArgs);
- 那么问题来了,如果原视频画质较高,那么截图可能会超过你的想象(1M及以上)
如何处理?
imagemin
imagemin-jpegtran
imagemin-pngquant
imagemin-webp
此处可自行选择使用imagemin-webp
or imagemin-pngquant
,我自己选择的是webp,因为webp的效率超乎你的想象,举个例子:1.5M经过imagemin-pngquant
压缩之后再使用imagemin-webp
,最后仅剩下区区20K。
// 待压缩的图片所在的文件夹
const sourcePath = path.resolve("./out/pic");
// 压缩后的图片的输出路径
const targetPath = path.resolve("./images");
/**
* 读取文件夹下的所有图片列表
* @param {Function} cb
*/
const getImagesList = (cb) => {
fs.readdir(sourcePath, (err, files) => {
if (!err) {
return cb(files.map((name) => path.join(sourcePath, name)));
}
console.log(err);
});
};
/**
* 压缩图片
* @param {Array} files
*/
const compress = async (files) => {
return await imagemin(files, {
destination: targetPath,
plugins: [
imageminJpegtran(),
// imageminPngquant({
// quality: [0.2, 0.2],
// }),
imageminWebp({
quality: 10,
})
],
});
};
const run = () => {
getImagesList(async (files) => {
for (let index = 0; index < files.length; index++) {
try {
console.log(`${index+1}/${files.length}`)
// 图片一张一张处理,降低处理失败的概率。
await compress([files[index]]);
} catch (error) {
console.log(error);
}
}
});
};
run();
/** 以上代码是copy的,忘了出处。sry */
前期工作准备完毕,那么就该直接开始操作了!
此处注意,如果你切割出来的图片较多,请搭建一个静态文件服务或者??,不要放到当前项目下,可能会导致打包失败!超过限制
- 根据原有思路,那么我们其实只需要去处理一下url就好了,那其实就是
<template>
<img v-for="i in 1000" :src="`xxxx.com/img${i}.webp`" v-if="activeIndex ===i">
</template>
....
data () {
return {
activeIndex:1
}
}
...
....
setInterVal(this.aciveIndex++,100)
但是这个会有什么问题呢?那就是图片加载速度跟不上你切换的速度,那么会咋样?
中间部分图片加载不出来就会导致看起来画面掉帧,卡顿等
并且,通过v-if控制图片,还会导致资源反复加载,如果有同学挂着睡觉一晚上,明早起来房子没了,车子没了。。。。
- 仔细观察一下滴滴官网,发现他们的并没有出现这种情况? 没有思考出为什么他们的没有这个情况?可能是请求头的原因?还是什么情况? 大家可以帮忙参考一下
这是我设置的返回响应
滴滴的返回值
目前我为了解决这个问题,使用了用js把图片二进制流获取到,再生成base64,这样虽然也会有请求狂刷,但是不会造成额外的流量开销。
const total = 900;
const isLoaded = ref(0);
const images = ref<Array<string>>([]);
async function preloadImages() {
for (let i = 1; i <= total; i++) {
const data = await apis.utils.getImages<Blob>(`girl${i}.webp`);
const url = URL.createObjectURL(new Blob([data],{type: 'image/webp'}));
images.value[i] = url;
isLoaded.value++;
if(isLoaded.value >= 50) {
changeActive();
}
}
}
// 自执行函数
(async () => {
await preloadImages();
})();
const imageUrl = computed(() => {
return images.value[active.value]
});
....
<template>
<img :src="imageUrl" />
</template>
预览地址
用手机端打开更好~
pc的加载没有做处理,可能会白屏一段时间~
2023-4-3更新
已修复请求刷新的问题。
可以访问地址测试,首屏未作加载动画,已跟滴滴动画一样,不会狂刷请求了。。
期间尝试过很多办法,去处理这个问题
通过map存储jsx,有这个url就去取值通过map存储Image通过隐藏dom节点,查找对应img,再克隆base64- 通过map存储Image对象,找到图片容器夫父节点,appendClild或者replaceChild处理
通过最后一种方式以解决~
完美撒花~
大家可以自行查看network,试试效果~
Ikun专属源码链接[gitlab](src/components/images-video/index.tsx · master · Administrator / vue2.7_vite_ts · GitLab (lvzy.xyz))
2023-10-13 更新
关于为什么要用图片,而不是video。或许有了一个答案
- 浏览器本土化
可能很多同学还不知道什么是本土化。 本土化的概念就是让你的浏览器能对你想要的操作进行预测,并添加到浏览器上。so,现在安卓上的各家浏览器百花齐放。比如: 检测到video标签,并且可播放的时候,直接强制给你添加浏览器厂商提供的控件,包括但不限于:投屏、全屏、下载、浏览器统一的控制条。so
- 有了以上概念,那么就可以知道在首页放一个video会变成啥样了。为了避免“被本土化”,选了图片强缓存.