使用语言:Vue + 原生 Js
需求前景:一个电子手册,根据不同种类的内容进行排版并分页
效果预览
翻页动画实现
这个过程主要还是使用的原生Js实现的
1. DOM 结构
DOM 结构上主要分为 2 大块,我们可以理解为书本的左右两侧,我们首先看到的就是右页,翻过去之后就变成了左页。然后页面中间需要添加我们要显示的内容( content )。
所以 DOM 结构可以先构建成这样:
<div class="file-page-w" ref="turn">
<div class="turn-wraper">
<div class="turn-page-left">
<div class="turn-page-left-content">
<!-- 左侧页内容 -->
</div>
</div>
<div class="turn-page-right">
<div class="turn-page-right-content">
<!-- 右侧页内容 -->
</div>
</div>
</div>
</div>
2. JS 逻辑
首先我们需要在页面实例初始化完成后挂载一些事件,这些事件将挂载在我们的翻页组件的父节点上 ref='trun',我们知道翻页主要的就是屏幕的滑动,所以需要监听的就是以下 3 个鼠标(光标)事件:
触控开始 -> 触动滑动 -> 触控结束
移动端:mousedown mousemove mouseup
Web端:touchstart touchmove touchend
由于移动端和Web端鼠标事件不同所以做了区分,但是功能逻辑是相同的。
思路如下:
- 触控开始 - 获取触控点坐标 x / y , 从触控点 x 坐标相对于屏幕宽度的位置判断这个触控动作是向前翻页还是向后翻页。 判断完成后,开始准备翻页过程动画并计算出翻页动画的样式值。
- 滑动触动 - 保存滑动路径,并暴露出在滑动中的方法给父组件使用
turning - 触控结束 - 正式翻页
数据溢出处理
首先获取到的数据有很多种类型的组合,这里的思路是:
- 根据屏幕宽度计算出 rem 计算比例
- 计算出页面可以容纳的 dom 高度
- 根据 rem,预先写好的 style ,动态创建 dom ,计算出固定 dom 的高度。
我们知道文本图片这种数据在同一条数据中可能是会有很多的,一夜很有可能塞不下,所以我们需要对数据进行拆分
接下来就是不同数据的超出计算过程辣~~
-
文本
首先把所有文本根据预先写好的样式创建 dom ,然后得出需要占用的高度(要预先写好样式是因为文本中可能有换行符等)。
fuction computedStyleHeight(className, content) { let dom = document.createElement('div'); if (className === 'cmt-cont') { dom.style.width = `${pageInfos.pageWidth}px`; } dom.className = className; dom.textContent = content; document.body.appendChild(dom); setTimeout(() => { document.body.removeChild(dom); }); return dom.clientHeight; }得出了总体高度后,如果这个总体高度,大于页面可以容纳的高度,则进行拆分,拆分步骤如下:
1. 页面可容纳高度:A 2. 文本总高度:B 3. 得出需要分的页数:C = B / A (可能会有误差,可以加上一个误差值) 4. 文本字符总长度:D,得出每一页的文本长度 E = D / C 5. 按照每页文本长度切割文本let infos = { article: { text: 'xxx', height: 1000 } }; let pageContHeight = 300; // 如果文字太长 拆 if (notes.hasOwnProperty('article') && infos['article'].height > pageContHeight) { let newArticArr = {}; let allHeight = infos['article'].height; let totalPage = Math.ceil(allHeight / pageContHeight); let singleTextLength = Math.ceil(infos['article'].text.length / totalPage); for (let k = 0; k < totalPage; k++) { let text = infos['article'].text.slice(k * singleTextLength, (k + 1) * singleTextLength); infos[`article${k}`] = { text: text, height: this.computedStyleHeight('test-text-height', text) } } delete infos['article']; }这样的计算过程就能对文本进行拆分辣~~
-
图片
将图片进行分组,4 个一组
let group = [....]; // 很多图片的数组 let len = group.length; let reGroupArr = []; for (let i = 0; i < len; i += 4) { reGroupArr.push(group.slice(i, i + 4)) // 每4项分成一组 }得到的 reGroupArr 就是4个图片一个数组的二维数组
然后根据 4 图的排列计算出需要占用的高度,并存储。
上面进行了简单的例子介绍,下面是具体塞进页面数据的过程:
假设最终拆分出来的一条数据长这个样子:
let datas = {
article1: {
text: 'xxx1',
height: 200
},
article2: {
text: 'xxx2',
height: 300
},
imgs1: {
imgs: ['url1', 'url2', 'url3', 'url4'],
height: 400
},
imgs2:{
imgs: ['url1', 'url2', 'url3', 'url4'],
height: 420
}
}
首先我们需要知道一个页面的容纳高度:然后把数据依次循环,
可以放入的高度 - 就 push 进去 同时减去 push 的元素的高度 超出高度 - 页面下标+1,可容纳高度恢复初始值,塞入下一页
代码实现如下:
let newArr = [];
let i = 0;
let surplusHeight = fullHeight; // 剩余高度
Object.keys(infos).forEach((key, index) => {
if ($type.isObject(infos[key]) && infos[key]['height'] && surplusHeight >= infos[key].height) {
newArr[i][key] = infos[key];
surplusHeight = surplusHeight - infos[key].height;
} else if ($type.isObject(infos[key]) && infos[key]['height'] && surplusHeight < infos[key].height) {
++i;
newArr[i] = {
contentPage: pageInfos.contentType.contentPage
};
surplusHeight = fullHeight;
newArr[i][key] = infos[key];
surplusHeight = surplusHeight - infos[key].height;
}
});
return newArr;
这里最后得出的 newArr 就是我们拆分出来的每一页的页面数据。