Commit 5dde357d authored by jyx's avatar jyx

代码优化

parent 528d4299
...@@ -29,7 +29,8 @@ export default class Book { ...@@ -29,7 +29,8 @@ export default class Book {
free, free,
charge, charge,
bookLegumes, bookLegumes,
lockRate lockRate,
articleChapterList
} = param || {} } = param || {}
this.id = id; this.id = id;
this.title = title; this.title = title;
...@@ -62,5 +63,6 @@ export default class Book { ...@@ -62,5 +63,6 @@ export default class Book {
this.free = free; this.free = free;
this.bookLegumes = bookLegumes; this.bookLegumes = bookLegumes;
this.lockRate = lockRate; this.lockRate = lockRate;
this.articleChapterList = articleChapterList;
} }
} }
\ No newline at end of file
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
data: function() { data: function() {
return { return {
fontSize: '18', fontSize: '18',
backgroundColor: "#fff", backgroundColor: "#000",
catalogue: 1, catalogue: 1,
} }
}, },
...@@ -117,10 +117,11 @@ ...@@ -117,10 +117,11 @@
isStart: chapterId == 1, //是否是第一章节 isStart: chapterId == 1, //是否是第一章节
isEnd: false, //是否是最后一个章节 isEnd: false, //是否是最后一个章节
}] }]
const { // const {
page // page
} = this.$refs; // } = this.$refs;
page.change({ console.log('AAAAAA', page)
this.$refs.page.change({
contents: contents, contents: contents,
currentChapter: chapterId, //从第二章节开始阅读 和章节chapter对应 currentChapter: chapterId, //从第二章节开始阅读 和章节chapter对应
start: 0 //从章节的第100个字开始阅读 start: 0 //从章节的第100个字开始阅读
......
## 1.6.4(2024-07-30)
* 增加加载提示文字的属性配置
## 1.6.3(2024-07-29)
* 替换滚动模式上拉加载为触底加载,解决app端上拉加载无法触发的问题
## 1.6.2(2024-06-21)
* 修复yingbing-text-reader空格不显示的问题
* 修复yingbing-text-reader底部文本会被切割的问题
## 1.6.1(2024-05-28)
* 这是更新为重构插件,不想折腾的同学可以不用更新
* 兼容了vue3
* 将插件功能细分为了3个组件,具体看下面的文档
* 这个版本必须下载最新版的yingbing-flip插件才能使用,如果你想使用翻页阅读的话
## 1.6.0(2023-12-22)
* measureSize增加藏文字符测量
* 增加charSize属性单独定义字符尺寸
* split属性增加多字符兼容
## 1.5.9(2023-09-01)
* 修复滚动模式下计算滚动位置异常的问题
## 1.5.8(2023-08-23)
* 修复APP-VUE翻页翻往下一章时可能会出现的undefined
## 1.5.7(2023-08-21)
* 增加split属性,用于分隔字符串排版,适用于英文小说
* measureSize属性中将英文字母大小拆分成小写英文字母和大写英文字母大小
## 1.5.6(2023-07-24)
* 解决bgColor属性无效的问题
## 1.5.5(2023-07-22)
* 请注意此次更新将翻页功能抽出来,插件本身只有滚动阅读功能,如果需要翻页功能请下载新插件好用翻页组件配合使用,下载地址看使用介绍
* 此次更新未更新新功能,不想多下载插件的朋友可以不更新
## 1.5.4(2023-07-10)
* 增加firstTipUnable、lastTipUnable属性控制第一页提示页和最后一页提示页的显示
## 1.5.3(2023-07-08)
* 自定义插槽添加作用域插槽,可以获取当前页信息,用于动态绑定内容(不支持微信小程序)
## 1.5.2(2023-07-07)
* 增加firstTip,lastTip属性自定义第一页和最后一页提示文字
* 增加top、bottom插槽,自定义第一页和最后一页的页面
## 1.5.1(2023-07-07)
* 新增unableClickPage属性控制点击左右2侧翻页
## 1.5.0(2023-06-20)
* 修复微信小程序报错的问题
## 1.4.9(2023-06-14)
* 修复向前翻页页面抖动的问题
* 优化loadmore事件返回回调为fail或者timeout时的显示
* 优化change事件,不再强制传contents
## 1.4.8(2023-06-12)
* 修复h5,APP-VUE自定义页面添加点击事件无法点击的问题
## 1.4.7(2023-06-07)
* 优化h5电量显示,避免报错
## 1.4.6(2023-06-07)
* 新增自动翻页功能
* 优化页面刷新逻辑,减少报错
## 1.4.5(2023-05-27)
* 添加字符宽度自动测量功能,减少排版错误
* 新增measureSize属性,用于自定义字符宽度
## 1.4.4(2023-05-26)
* 翻页模式添加touchcancel事件
* 为兼容IOS空格计算宽度增加1px
## 1.4.3(2023-05-25)
* 新增fontFamily属性设置字体名称
* 新增fontFace属性添加自定义字体
## 1.4.2(2023-05-25)
* 修复IOS电量显示
## 1.4.1(2023-05-24)
* 修复单章节内容太短可能会引起得卡死和报错
## 1.4.0(2023-04-04)
* 优化翻页模式,减少闪烁问题
## 1.3.9(2023-04-03)
* 优化APP端电量获取,修复获取电量可能会造成闪退的问题
* 修复APP-NVUE翻页模式加载下一章节会闪烁的问题
## 1.3.8(2023-04-01)
* 修复APP-NVUE 向前翻页会闪烁的问题
* 删除一些不必要的代码文件
## 1.3.7(2023-04-01)
* APP-NVUE使用webview替代rich-text实现自定义页面
* APP-NVUE自定义页面支持添加自定义点击事件
* 稍微优化了一下翻页
## 1.3.6(2023-03-15)
* 修复在低端机上,分页会卡住的问题
## 1.3.5(2023-03-14)
* 优化整书模式的分页
## 1.3.4(2023-03-13)
* 修复APP-NVUE一些问题
## 1.3.3(2023-03-13)
* 优化滚动模式定位问题
## 1.3.2(2023-03-01)
* 修复整书模式 init初始化 无效果的 bug
## 1.3.1(2023-01-04)
* 修复直接点击上一页,文字不显示的bug
## 1.3.0(2022-12-20)
* 新增APP-NVUE和微信小程序支持
* 新增自定义插槽功能
* 整书模式已整合进入章节模式,表现效果和章节模式相同
* 新增页面顶部和底部的电池电量、章节、事件,页数显示
## 1.2.9(2022-09-06)
* 修复滑动过快造成翻页动画异常的bug
* 修复调用refresh方法报错的bug
* 修复翻页动画有时候方向判断错误的bug
* 滚动模式不再使用better-scroll插件,改用自己写的滚动组件
* 添加pageNext, pagePrev翻页方法
## 1.2.8(2022-06-08)
* 更改整书模式的翻页方法(上次更新忘记改了)
## 1.2.7(2022-06-04)
* 添加自定义页面功能(可用于付费章节、广告展示等)
* 更改触摸翻页方式,现在页面上任何地方都能触摸翻页
* 减小了翻页需要的距离
* 减少了翻页节流时间
## 1.2.6(2022-02-14)
* 修复向前点击翻页出现2种动画的bug
## 1.2.5(2021-10-19)
* 优化整书模式一些问题
## 1.2.4(2021-10-18)
* 修复滚动模式定位问题
## 1.2.3(2021-10-17)
* 添加点击区域配置,具体见下面得介绍
## 1.2.2(2021-10-17)
* 优化滚动模式
## 1.2.1(2021-10-15)
* 优化滚动模式逻辑
## 1.2.0(2021-10-15)
* 修复滚动模式下得问题
## 1.1.9(2021-10-15)
* 修复一些显示问题
* 优化了一下滚动模式滚动效果
## 1.1.8(2021-10-14)
* content对象中可以带上章节名称,方便取值
* 增加初始化loading效果
## 1.1.7(2021-10-14)
* 增加整书模式返回得页面信息
## 1.1.6(2021-10-14)
* 更改整书模式返回章节集合得参数
## 1.1.5(2021-10-14)
* 优化翻页得性能,同时最多显示3张页面
## 1.1.4(2021-10-13)
修复一些问题
## 1.1.3(2021-10-13)
* 修复一些问题
## 1.1.2(2021-10-13)
* 修复一些问题
## 1.1.1(2021-10-13)
* 修复了一些问题
## 1.1.0(2021-10-12)
* 此次更新为重构插件
* 首先感谢一下2位插件使用者提供的反馈
* 主要更新如下:
- 1、为请求事件增加了过渡效果,滚动模式下增加上拉和下拉操作
- 2、解决了一些逻辑问题,现在preload和loadmore事件触发更合理
- 3、其它都是些小更新,比如章节模式下currentChange事件增加了返回的页面信息
- 4、更改了参数结构,请注意阅读使用须知
## 1.0.5(2021-09-22)
* 修复上次更新引起无法翻到上一页的问题
## 1.0.4(2021-09-22)
* 修复横向翻页部分页面不绘制的bug
## 1.0.3(2021-09-05)
* 修复整书模式下阅读小说到最后面,下一页会从小说最前面再次排版的bug
## 1.0.2(2021-09-04)
* 修复预加载章节内容无效的问题
* 添加初始化触发currentChange事件
## 1.0.1(2021-08-29)
* 减少滚动模式下触底加载更多时的滚动距离
## 1.0.0(2021-08-28)
* 发布1.0.0版本
<template>
<view class="yingbing-battery">
<view class="yingbing-battery-wrapper" :style="{
'border-color': color
}">
<view class="yingbing-battery-content">
<view class="yingbing-battery-content-value" :style="{
'background-color': color,
width: value + 'px'
}">
</view>
</view>
</view>
<view class="yingbing-battery-top" :style="{
'background-color': color
}"></view>
</view>
</template>
<script>
const max = 16
export default {
inject: ['getColor'],
computed: {
color () {
return this.getColor()
}
},
data () {
return {
value: max
}
},
// #ifdef APP-PLUS
beforeDestroy() {
if ( this.recevier ) {
plus.android.runtimeMainActivity().unregisterReceiver(this.recevier)
}
},
// #endif
created () {
this.getBattery()
},
methods: {
getBattery () {
// #ifdef H5
//window.navigator.getBattery只能在安全环境下(比如:https file:///url)使用,判断一下避免报错
window.navigator.getBattery && window.navigator.getBattery().then((res) => {
// 电池电量在0到1之间,因此我们将其乘以100得出百分比
this.value = res.level * max
});
// #endif
// #ifdef MP-WEIXIN
wx.getBatteryInfo({
success: (res) => {
this.value = (res.level / 100) * max
}
})
// #endif
// #ifdef APP-PLUS
uni.getSystemInfo({
success: (res) => {
if ( res.osName == 'android' ) {
const main = plus.android.runtimeMainActivity()
const Intent = plus.android.importClass('android.content.Intent');
this.recevier = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', { 
onReceive: (context, intent) => { 
       let action = intent.getAction(); 
       if (action == Intent.ACTION_BATTERY_CHANGED) { 
            let level = intent.getIntExtra("level", 0); //电量 B5教程网 
this.value = (level / 100) * max
main.unregisterReceiver(this.recevier)//销毁注册广播
//let voltage = intent.getIntExtra("voltage", 0); //电池电压 
//let temperature = intent.getIntExtra("temperature", 0); //电池温度 
           //如需获取别的,在这里继续写,此代码只提供获取电量 
       } 
    } 
}); 
const filter = plus.android.newObject('android.content.IntentFilter', Intent.ACTION_BATTERY_CHANGED); 
main.registerReceiver(this.recevier, filter);
} else if ( res.osName == 'ios' ) {
const UIDevice = plus.ios.import("UIDevice");
const dev = UIDevice.currentDevice(); 
if (!dev.isBatteryMonitoringEnabled()) { 
    dev.setBatteryMonitoringEnabled(true); 
} 
let level = dev.batteryLevel();
this.value = level * max
}
}
})
// #endif
}
}
}
</script>
<style scoped>
.yingbing-battery {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
opacity: 0.5;
}
.yingbing-battery-wrapper {
width: 20px;
height: 9px;
border-width: 1px;
border-style: solid;
padding: 1px;
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
/* #endif */
}
.yingbing-battery-content {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
/* #endif */
}
.yingbing-battery-content-value {
height: 5px;
}
.yingbing-battery-content-value-text {
font-size: 8px;
}
.yingbing-battery-top {
width: 2px;
height: 5px;
}
</style>
\ No newline at end of file
<template>
<view class="yingbing-reader-footer">
<view class="yingbing-reader-footer-left">
<battery ref="battery"></battery>
<text class="yingbing-reader-footer-text" :style="{color: color}">{{time}}</text>
</view>
<text class="yingbing-reader-footer-text" :style="{color: color}"> {{progress ? progress + '%' : current + ' / ' + total }}</text>
</view>
</template>
<script>
import Battery from '../battery/battery.vue'
export default {
inject: ['getColor'],
components: {
Battery
},
props: {
total: {
type: [Number, String],
default: 0
},
current: {
type: [Number, String],
default: 0
},
progress: {
type: [Number, String],
default: 0
}
},
computed: {
color () {
return this.getColor()
}
},
data () {
return {
time: ''
}
},
created() {
this.getTime()
},
methods: {
zeroize (value) {
return value >= 10 ? value : '0' + value
},
getTime () {
const d = new Date()
this.time = this.zeroize(d.getHours()) + ':' + this.zeroize(d.getMinutes())
},
refresh () {
this.$refs.battery.getBattery()
this.getTime()
}
}
}
</script>
<style scoped>
.yingbing-reader-footer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 20px;
margin-top: 10px;
}
.yingbing-reader-footer-left {
display: flex;
flex-direction: row;
align-items: center;
}
.yingbing-reader-footer-text {
font-size: 10px;
opacity: 0.5;
margin-left: 10px;
}
</style>
\ No newline at end of file
<template>
<view class="yingbing-reader-header">
<view class="yingbing-reader-header-left">
<view class="yingbing-reader-header-back" :style="{'border-top-color': color, 'border-left-color': color}" v-if="backShow"
@touchstart.stop.prevent @touchmove.stop.prevent @touchend.stop.prevent="handleBack"
@mousedown.stop.prevent @mousemove.stop.prevent @mouseup.stop.prevent="handleBack"></view>
<text class="yingbing-reader-header-text" :style="{color: color}">{{title}}</text>
</view>
<text class="yingbing-reader-header-text" :style="{color: color}" v-if="progress">{{progress.toFixed(2)}}%</text>
</view>
</template>
<script>
import Battery from '../battery/battery.vue'
export default {
inject: ['getColor', 'getBackShow'],
props: {
title: {
type: String,
default: ''
},
progress: {
type: [Number, String],
default: 0
}
},
computed: {
color () {
return this.getColor()
},
backShow () {
return this.getBackShow()
}
},
methods: {
handleBack (e) {
e.preventDefault && e.preventDefault()
e.stopPropagation && e.stopPropagation()
uni.$emit('yingbing-reader-back')
}
}
}
</script>
<style>
.yingbing-reader-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
height: 20px;
}
.yingbing-reader-header-left {
display: flex;
flex-direction: row;
align-items: center;
}
.yingbing-reader-header-text {
font-size: 10px;
opacity: 0.5;
}
.yingbing-reader-header-back {
width: 6px;
height: 6px;
border-top-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-left-style: solid;
transform: rotateZ(-45deg);
margin-right: 5px;
opacity: 0.5;
}
</style>
\ No newline at end of file
<template>
<view v-if="visible" class="circle y-flex" ref="loading" :style="{
width: pixelSize + 'px',
height: pixelSize + 'px',
'border-radius': pixelSize + 'px'
}">
<view
class="line y-flex"
:style="{
'border-top-width': (pixelSize / 4) + 'px',
'border-bottom-width': (pixelSize / 4) + 'px',
'border-top-color': item.top,
'border-bottom-color': item.bottom,
width: (pixelSize / 12) + 'px',
left: ((pixelSize / 2) - (pixelSize / 24)) + 'px',
}"
:class="'line_' + index"
v-for="(item, index) in rgbs" :key="index"></view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const Binding = uni.requireNativePlugin('bindingx');
// #endif
export default {
props: {
visible: {
type: Boolean,
default: true
},
size: {
type: [Number, String],
default: 40
},
color: {
type: String,
default: '#333333'
}
},
computed: {
rgbs () {
const rgb = this.hexToRgb(this.color).replace('rgb(', '').replace(')', '')
return [{
top: `rgba(${rgb}, 1)`,
bottom: `rgba(${rgb}, .4)`
},{
top: `rgba(${rgb}, .4)`,
bottom: `rgba(${rgb}, .5)`
},{
top: `rgba(${rgb}, .4)`,
bottom: `rgba(${rgb}, .6)`
},{
top: `rgba(${rgb}, .4)`,
bottom: `rgba(${rgb}, .7)`
},{
top: `rgba(${rgb}, .4)`,
bottom: `rgba(${rgb}, .8)`
},{
top: `rgba(${rgb}, .4)`,
bottom: `rgba(${rgb}, .9)`
}]
},
pixelSize () {
return this.unitpixel(this.size)
}
},
data () {
return {
loading_binding: null
}
},
mounted() {
// #ifdef APP-NVUE
this.$nextTick( function () {
if ( this.visible ) this.start()
})
// #endif
},
beforeDestroy() {
// #ifdef APP-NVUE
if ( this.loading_binding ) {
Binding.unbind({
token: this.loading_binding.token,
eventType: 'timing'
})
this.loading_binding = null
}
// #endif
},
methods: {
start () {
const loading = this.getEl(this.$refs.loading);
this.loading_binding = Binding.bind({
eventType: 'timing',
props: [{
element: loading,
property: 'transform.rotateZ',
expression: 'floor(t/100)*30'
}]
});
},
unitpixel (size) {
const text = size.toString()
return text.indexOf('rpx') > -1 ? uni.upx2px(text.replace('rpx', '')) : text.indexOf('px') > -1 ? parseFloat(text.replace('px', '')) : uni.upx2px(text)
},
hexToRgb (hex) {
hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
let str="rgb("
const r = parseInt(hex.slice(1,3),16).toString(); //ff slice不包括end
const g = parseInt(hex.slice(3,5),16).toString(); //00
const b = parseInt(hex.slice(5,7),16).toString(); //ff
str += r+","+g+","+b+")";
return str
},
getEl (el) {
if (typeof el === 'string' || typeof el === 'number') return el;
if (WXEnvironment) {
return el.ref;
} else {
return el instanceof HTMLElement ? el : el.$el;
}
}
},
watch: {
visible (newVal) {
// #ifdef APP-NVUE
this.$nextTick(() => {
if ( newVal ) {
this.start()
} else {
if ( this.loading_binding ) {
Binding.unbind({
token: this.loading_binding.token,
eventType: 'timing'
})
this.loading_binding = null
}
}
})
// #endif
}
}
}
</script>
<style scoped>
/* #ifndef APP-NVUE */
@keyframes loading{
0% {
transform: rotateZ(30deg);
}
9.33333%{
transform: rotateZ(60deg);
}
18.66666%{
transform: rotateZ(90deg);
}
27.99999%{
transform: rotateZ(120deg);
}
37.33332%{
transform: rotateZ(150deg);
}
46.66665%{
transform: rotateZ(180deg);
}
55.99998%{
transform: rotateZ(210deg);
}
65.33331%{
transform: rotateZ(240deg);
}
74.66664%{
transform: rotateZ(270deg);
}
83.99997%{
transform: rotateZ(300deg);
}
93.33333%{
transform: rotateZ(330deg);
}
100%{
transform: rotateZ(360deg);
}
}
/* #endif */
.circle {
position: relative;
/* #ifndef APP-NVUE */
animation: loading 1200ms step-start infinite;
/* #endif */
}
.circle .line {
position: absolute;
border-top-style: solid;
border-bottom-style: solid;
top: 0;
bottom: 0;
}
.circle .line_0 {
}
.circle .line_1 {
transform: rotateZ(30deg);
}
.circle .line_2 {
transform: rotateZ(60deg);
}
.circle .line_3 {
transform: rotateZ(90deg);
}
.circle .line_4 {
transform: rotateZ(120deg);
}
.circle .line_5 {
transform: rotateZ(150deg);
}
</style>
\ No newline at end of file
export default {
provide () {
return {
getPrevChapterDefaultText: () => this.prevChapterDefaultText,
getNextChapterDefaultText: () => this.nextChapterDefaultText,
getChapterReadyText: () => this.chapterReadyText,
getChapterLoadingText: () => this.chapterLoadingText,
getChapterSuccessText: () => this.chapterSuccessText,
getChapterFailText: () => this.chapterFailText,
getPrevChapterEndText: () => this.prevChapterEndText,
getNextChapterEndText: () => this.nextChapterEndText
}
},
props: {
loadingText: {
type: String,
default: '内容排版中'
},
errorText: {
type: String,
default: '渲染失败\n\n点击重试'
},
chapterReadyText: {
type: String,
default: '松开加载'
},
chapterLoadingText: {
type: String,
default: '正在加载'
},
chapterSuccessText: {
type: String,
default: '加载成功'
},
chapterFailText: {
type: String,
default: '加载失败'
},
prevChapterDefaultText: {
type: String,
default: '加载上一章'
},
prevChapterEndText: {
type: String,
default: '前面没有了'
},
nextChapterDefaultText: {
type: String,
default: '加载下一章'
},
nextChapterEndText: {
type: String,
default: '后面没有了'
}
}
}
\ No newline at end of file
const threshold = 50
export default {
props: {
//点击事件位置设置
clickOption: {
type: Object,
default () {
return {
width: uni.upx2px(200),
height: uni.upx2px(200),
left: 'auto',
top: 'auto'
}
}
}
},
computed: {
//点击区域左边位置
clickLeft () {
return typeof this.clickOption.left == 'number' ? this.clickOption.left : (this.windowWidth / 2) - (this.clickOption.width / 2)
},
//点击区域右边位置
clickRight () {
return this.clickLeft + this.clickOption.width
},
//点击区域顶部位置
clickTop () {
return typeof this.clickOption.top == 'number' ? this.clickOption.top : (this.windowHeight / 2) - (this.clickOption.height / 2)
},
//点击区域底部位置
clickBottom () {
return this.clickTop + this.clickOption.height
}
},
data () {
return {
autoplaySync: false,
windowHeight: 0,//组件高度
windowWidth: 0,//组件宽度
clickTime: 0,//点击次数
touchstartX: 0,//触摸开始x轴位置
touchstartY: 0,//触摸开始y轴位置
touchmoveX: 0,//触摸滑动x轴距离
touchmoveY: 0,//触摸滑动y轴距离
touchTime: 0//触摸时间
}
},
methods: {
touchstart (e) {
if ( this.pageType != 'scroll' ) this.autoplaySync = false//如果是翻页阅读关闭自动播放
if ( this.touchTimer ) {//清楚定时任务
clearTimeout(this.touchTimer)
this.touchTimer = null
}
if ( this.longTimer ) {//清楚定时任务
clearTimeout(this.longTimer)
this.longTimer = null
}
const touch = e.touches[0]
this.touchstartX = touch.pageX
this.touchstartY = touch.pageY
//点击在规定区域中
if ( this.touchstartX >= this.clickLeft && this.touchstartX <= this.clickRight && this.touchstartY >= this.clickTop && this.touchstartY <= this.clickBottom) {
this.clickTime++
if ( this.clickTime == 1 ) {//第一次点击
this.touchTimer = setTimeout(() => {
this.touchTime = 300
}, 300)
this.longTimer = setTimeout(() => {
this.touchTime = 500
if ( this.touchmoveX <= threshold && this.touchmoveY <= threshold ) this.$emit('long-click')//抛出长按事件
this.resetTouch()
}, 500)
}
} else {//没有点击在规定区域中直接重置触摸事件
this.resetTouch()
}
},
touchmove (e) {
if ( this.clickTime > 0 ) {
const touch = e.touches[0]
this.touchmoveX = Math.abs(touch.pageX - this.touchstartX)
this.touchmoveY = Math.abs(touch.pageY - this.touchstartY)
}
},
touchend (e) {
if ( this.pageType != 'scroll' ) this.autoplaySync = this.autoplay//如果是横向翻页开启自动播放
if ( this.touchTimer ) {//清楚按下定时任务
clearTimeout(this.touchTimer);
this.touchTimer = null
}
if ( this.longTimer ) {//清楚长按定时任务
clearTimeout(this.longTimer)
this.longTimer = null
}
//点击次数为零或者触摸时间超过300,或者滑动距离超过阙值
if ( this.clickTime == 0 || this.touchTime > 300 || this.touchmoveX > threshold || this.touchmoveY > threshold ) {
this.resetTouch()
return
}
if ( this.clickTime == 1 ) {//第一次点击
this.touchTimer = setTimeout(() => {
this.$emit('single-click')//抛出单击事件
this.resetTouch()
}, 300)
}
if ( this.clickTime == 2 ) {//第二次点击
this.$emit('double-click')//抛出双击事件
this.resetTouch()
}
},
resetTouch () {
this.touchstartX = 0
this.touchstartY = 0
this.touchmoveX = 0
this.touchmoveY = 0
this.touchTime = 0
this.clickTime = 0
}
}
}
\ No newline at end of file
var max = 200
var refreshHeight = 80
function touchstart(event, ins) {
console.log('touchstart');
var state = ins.getState()
if ( state.refresh ) return
var touch = event.touches[0] || event.changedTouches[0]
state.startX = touch.pageX
state.startY = touch.pageY
}
function touchmove(event, ins) {
console.log('touchmove');
var state = ins.getState()
if ( state.refresh ) return
if ( state.startY > 0) {
var touch = event.touches[0] || event.changedTouches[0]
if ((Math.abs(touch.pageY - state.startY) > Math.abs(touch.pageX - state.startX)) && Math.abs(touch.pageY - state.startY) > 20) {
var pageY = touch.pageY
var rate = max / (max + Math.abs(pageY - state.startY))
state.threshold = rate * (pageY - state.startY)
if ( state.threshold > 0 && !state.pulldownable ) return
if ( state.threshold < 0 && !state.pullupable ) return
if ( state.threshold > max ) {
state.threshold = max
}
if ( state.threshold < -max ) {
state.threshold = -max
}
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(' + state.threshold + 'px)',
transition: ''
})
if ( state.threshold > 0 ) {
ins.callMethod('pullingdown', state.threshold)
} else {
ins.callMethod('pullingup', Math.abs(state.threshold))
}
}
}
}
function touchend(event, ins) {
touchaction(event, ins)
}
function touchcancel(event, ins) {
touchaction(event, ins)
}
function touchaction (event, ins) {
var state = ins.getState()
if ( state.refresh ) return
if ( state.threshold > 0 && state.pulldownable ) {//下拉
if ( state.threshold > refreshHeight ) {//满足下拉条件
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(' + refreshHeight + 'px)',
transition: 'transform .1s'
})
ins.callMethod('pulldown')
state.refresh = true
} else {//不满足下拉条件恢复样式
stop(ins)
}
}
if ( state.threshold < 0 && state.pullupable ) {//上拉
if ( Math.abs(state.threshold) > refreshHeight ) {//满足上拉条件
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(-' + refreshHeight + 'px)',
transition: 'transform .1s'
})
ins.callMethod('pullup')
state.refresh = true
} else {//不满足上拉条件恢复样式
stop(ins)
}
}
}
function stop (ins) {
var state = ins.getState()
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(0)',
transition: 'transform .1s'
})
state.threshold = 0
state.startY = 0
state.refresh = false
}
function refreshStateWatcher (newVal, oldVal, ins) {
if ( newVal == 'pulldown' ) {
var state = ins.getState()
state.threshold = refreshHeight + 1
state.refresh = false
touchaction(null, ins)
}
if ( newVal == 'pullup' ) {
var state = ins.getState()
state.threshold = -(refreshHeight + 1)
state.refresh = false
touchaction(null, ins)
}
if ( newVal == 'stop' ) stop(ins)
}
function pulldownableWatcher (newVal, oldVal, ins) {
var state = ins.getState()
if ( state ) state.pulldownable = newVal
}
function pullupableWatcher (newVal, oldVal, ins) {
var state = ins.getState()
if ( state ) state.pullupable = newVal
}
module.exports = {
refreshStateWatcher: refreshStateWatcher,
pulldownableWatcher: pulldownableWatcher,
pullupableWatcher: pullupableWatcher,
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
touchcancel: touchcancel
}
\ No newline at end of file
<template>
</template>
<script>
</script>
<style>
</style>
\ No newline at end of file
<template>
<view class="yingbing-computed" :style="[computedStyle]">
<reader-header :text="chapter.title || ''" v-if="headerShowSync"></reader-header>
<web-view ref="webview" style="flex:1" src="/uni_modules/yingbing-ReadPage/hybrid/html/computed.html" @onPostMessage="onPostMessage"></web-view>
<reader-footer v-if="footerShowSync"></reader-footer>
</view>
</template>
<script>
import ReaderHeader from '../../header/header.vue'
import ReaderFooter from '../../footer/footer.vue'
export default {
inject: ['fontSize', 'fontFamily', 'lineHeight', 'topGap', 'bottomGap', 'slide', 'headerShow', 'footerShow', 'totalChapter'],
components: {
ReaderHeader,
ReaderFooter
},
computed: {
computedStyle () {
return {
'padding-top': this.topGap + 'px',
'padding-bottom': this.bottomGap + 'px',
'padding-left': this.slide + 'px',
'padding-right': this.slide + 'px'
}
}
},
data () {
return {
chapter: {},//章节内容临时存储
success: null,//成功回调
fail: null,//失败回调,
headerShowSync: false,//展示头部
footerShowSync: false//展示底部
}
},
beforeDestroy() {
this.$refs.webview && this.$refs.webview.evalJS("destroyHandleTimer()")
},
methods: {
start ({chapter, success, fail}) {
this.chapter = chapter
this.success = success
this.fail = fail
this.headerShowSync = typeof chapter.headerShow == 'boolean' ? chapter.headerShow : this.headerShow//判断是否显示头部
this.footerShowSync = typeof chapter.footerShow == 'boolean' ? chapter.footerShow : this.footerShow//判断是否显示底部
const style = {fontSize: this.fontSize, fontFamily: this.fontFamily, lineHeight: this.lineHeight, totalChapter: this.totalChapter}
this.$nextTick(function () {
setTimeout(() => {
this.$refs.webview && this.$refs.webview.evalJS("start(" + encodeURIComponent(JSON.stringify(chapter)) + ', ' + encodeURIComponent(JSON.stringify(style)) + ")")
}, 100)
})
},
onPostMessage (e) {
e.detail.data.forEach(item => {
if ( item.handleSuccess ) {
this.success && this.success(item.handleSuccess)
this.success = null
this.fail = null
this.chapter = {}
}
})
}
}
}
</script>
<style scoped>
.yingbing-computed {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transform: translateX(-1000px);
}
</style>
\ No newline at end of file
<template>
<web-view :src="'/uni_modules/yingbing-ReadPage/hybrid/html/content.html?content=' + encodeURIComponent(contentSync)"></web-view>
</template>
<script>
export default {
inject: ['color', 'fontSize', 'fontFamily', 'lineHeight', 'selectable'],
props: {
item: {
type: Object,
default () {
return new Object
}
}
},
computed: {
contentSync () {
let content = this.item.content || ''
content = content.replace('<whole-render', '<div').replace('<\/whole-render>', '<\/div>')
return `<div class="yingbing-reader-content-html ${this.selectable ? 'user-selectable' : 'user-selectclose'}" style="box-sizing: border-box;color:${this.color};font-family:${this.fontFamily};font-size:${this.fontSize}px;line-height:${this.lineHeight}px;">` + content + '</div>'
}
}
}
</script>
<style>
</style>
\ No newline at end of file
export default {
methods: {
//判断是否显示头部
getFlipHeaderShow (item) {
return ['text', 'slot'].includes(item.type) ? typeof item.headerShow == 'boolean' ? item.headerShow : this.headerShow : false
},
//判断是否显示底部
getFlipFooterShow (item) {
return ['text', 'slot'].includes(item.type) ? typeof item.footerShow == 'boolean' ? item.footerShow : this.footerShow : false
},
//渲染页面
flipRender ({chapter, current, start = 0}) {
if ( chapter ) {//如果传入章节内容
const index = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (index > -1) this.chapters[index] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
}
current = parseInt(current || 0)//强制转换类型为int
start = parseInt(start)//强制转换类型为int
this.pages = [{index: current, type: 'loading'}]//显示loading
this.current = 0//重置current
this.$refs.flip.refresh()
const cgs = this.chapters.filter(c => c.index == current || c.index == current - 1 || c.index == current + 1)//筛选出符合条件的三个章节内容
let index = 0, arr = []
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
const handle = () => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: cgs[index],
success: pages => {
arr = arr.concat(pages)
if ( index < cgs.length - 1 ) {
index++
this.$nextTick(function () { handle() })
} else {
this.computeds.splice(computedIndex, 1)
this.pages = this.handlePages(arr)
const pageIndex = this.pages.findIndex(p => start >= p.start && start < p.end && p.index == current )//定位章节
if ( pageIndex == -1 ) this.current = this.pages.findIndex(p => start >= p.end && p.index == current )//定位章节
else this.current = pageIndex
this.$nextTick(function () {
this.$refs.flip.refresh()
this.handleChange({current: this.current, detail: this.pages[this.current] })
this.autoplaySync = this.autoplay
})
}
}
})
}
handle()
}, 100)
})
},
//渲染下一章节
handleFlipLoadRender (status, chapter, callback, isPrev) {
if (status == 'success') {//获取内容成功
const nowIndex = isPrev ? this.pages[0].index : this.pages[this.pages.length-1].index
const chapterIndex = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (chapterIndex > -1) this.chapters[chapterIndex] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: chapter,
success: p => {
this.computeds.splice(computedIndex, 1)
const pages = isPrev ? p.concat(this.pages) : this.pages.concat(p)//添加新计算的章节内容
this.pages = pages//渲染章节内容
if ( isPrev ) {//如果是加载上一章节需要重新定位
this.current = pages.findIndex(page => page.index == nowIndex)//定位页面
this.$refs.flip.refresh()//刷新翻页组件
}
this.chapterLoading = false//关闭章节加载等待
callback && callback(status)//关闭加载动画
this.handleChange({current: this.current, detail: this.pages[this.current]})//触发change事件
}
})
}, 100)
})
} else {
this.chapterLoading = false//关闭章节加载等待
callback && callback(status)//关闭加载动画
}
}
}
}
\ No newline at end of file
<template>
<view class="yingbing-content" v-html="contentSync"></view>
</template>
<script>
export default {
inject: ['getFontSize', 'getFontFamily', 'getLineHeight', 'getColor', 'getSelectable'],
props: {
item: {
type: Object,
default () {
return new Object
}
}
},
computed: {
color () {
return this.getColor()
},
fontSize () {
return this.getFontSize()
},
fontFamily () {
return this.getFontFamily()
},
lineHeight () {
return this.getLineHeight()
},
selectable () {
return this.getSelectable()
},
contentSync () {
let content = this.item.content || ''
content = content.replace('<whole-render', '<div').replace('<\/whole-render>', '<\/div>')
return `<div class="yingbing-reader-content-html ${this.selectable ? 'user-selectable' : 'user-selectclose'}" style="box-sizing: border-box;color:${this.color};font-family:${this.fontFamily};font-size:${this.fontSize}px;line-height:${this.lineHeight}px;">` + content + '</div>'
}
},
data () {
return {
selectShow: false,
selectTop: 0,
selectLeft: 0,
selectArrowLeft: 0
}
},
mounted() {
// #ifdef H5
// document.addEventListener('selectionchange',() => {
// const selection = window.getSelection();// 获取当前的选区对象
// // 如果选区不为空
// if (selection.toString()) {
// // 获取选区中的第一个范围
// const range = selection.getRangeAt(0);
// // 将选区的边界用于定位操作框
// const rect = range.getBoundingClientRect();
// const box = document.querySelector('.yingbing-reader-select-box')
// this.selectArrowLeft = rect.left
// this.selectTop = rect.top + (box.offsetHeight / 2)
// this.selectLeft = rect.left - (box.offsetWidth / 2)
// const right = box.offsetWidth + this.selectLeft
// const bottom = box.offsetHeight + this.selectTop
// if ( right > document.body.offsetWidth ) this.selectLeft = this.selectLeft - (right - document.body.offsetWidth)
// if ( bottom > document.body.offsetHeight ) this.selectTop = this.selectTop - (bottom - document.body.offsetHeight)
// this.selectShow = true
// } else {
// this.selectShow = false
// }
// });
// #endif
}
}
</script>
<style scoped>
/deep/ .yingbing-img {
max-width: 100%!important;
box-sizing: border-box!important;
}
/deep/ .user-selectable {
-webkit-user-select: text; /*webkit浏览器*/
-khtml-user-select: text; /*早期浏览器*/
-moz-user-select: text; /*火狐*/
-ms-user-select: text; /*IE10*/
user-select: text;
}
/deep/ .user-selectclose {
-webkit-touch-callout: none; /*系统默认菜单被禁用*/
-webkit-user-select: none; /*webkit浏览器*/
-khtml-user-select: none; /*早期浏览器*/
-moz-user-select: none; /*火狐*/
-ms-user-select: none; /*IE10*/
user-select: none;
}
.yingbing-content {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.yingbing-reader-select-box {
position: fixed;
display: flex;
flex-direction: row;
background-color: #fff;
box-shadow: 0 0 10px rgba(0,0,0,.1);
border-radius: 5px;
padding: 15px 10px;
}
.yingbing-reader-select-box .arrow {
position: absolute;
width: 0;
height: 0;
border-top: 6px solid transparent;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
top: -12px;
}
.yingbing-reader-select-box .select-box-item {
padding: 0 10px;
font-size: 13px;
flex-shrink: 0;
}
</style>
\ No newline at end of file
export default {
computed: {
currentProgress () {
return this.pages[this.current]?.progress || 0
},
currentTitle () {
return this.pages[this.current]?.title || this.title
},
currentTotal () {
return this.pages[this.current]?.total || 0
},
currentPage () {
return this.pages[this.current]?.current || 0
}
},
methods: {
//渲染页面
scrollRender ({chapter, current, start = 0}) {
if ( chapter ) {//如果传入章节内容
const index = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (index > -1) this.chapters[index] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
}
current = parseInt(current || 0)//强制转换类型为int
start = parseInt(start)//强制转换类型为int
this.pages = [{index: current, type: 'loading'}]//显示loading
this.current = 0//重置current
const cgs = this.chapters.filter(c => c.index == current || c.index == current - 1 || c.index == current + 1)//筛选出符合条件的三个章节内容
let index = 0, arr = []
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
const handle = () => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: cgs[index],
success: pages => {
arr = arr.concat(pages)
if ( index < cgs.length - 1 ) {
index++
this.$nextTick(function () { handle() })
} else {
this.computeds.splice(computedIndex, 1)
this.pages = this.handlePages(arr)
const pageIndex = this.pages.findIndex(p => start >= p.start && start < p.end && p.index == current )//定位章节
if ( pageIndex == -1 ) this.current = this.pages.findIndex(p => start >= p.end && p.index == current )//定位章节
else this.current = pageIndex
this.$nextTick(function () {
this.$refs.scroll.resetRefresh()
this.$refs.scroll.scrollToIndex(this.current)
this.handleChange({current: this.current, detail: this.pages[this.current] })
this.$nextTick(function () {
this.autoplaySync = this.autoplay
})
})
}
}
})
}
handle()
}, 100)
})
},
//翻页改变事件
async handleScroll (e) {
const scrollTop = e.detail.scrollTop
const scrollHeight = e.detail.scrollHeight
if ( !this.scrolling ) {
try{
this.scrolling = true
const rate = Math.floor(scrollTop / this.contentHeight)
let maybe = this.pages[rate] ? rate : this.pages.length-1
let top = -1
while ( top < 0 && maybe < this.pages.length - 1 ) {
const itemRect = await this.$refs.scroll.getItemRect(maybe)
top = itemRect.top
top < 0 ? maybe++ : null
}
const current = top >= 0 ? maybe : this.pages.length - 1
if ( current != this.current ) {
this.handleChange({current: current, detail: this.pages[current]})
this.$refs.footer && this.$refs.footer.refresh()
}
this.scrolling = false
}catch(e){
this.scrolling = false
}
}
},
//渲染下一章节
handleScrollLoadRender (status, chapter, callback, isPrev) {
if ( status == 'success' ) {//获取内容成功
const nowIndex = isPrev ? this.pages[0].index : this.pages[this.pages.length-1].index
const chapterIndex = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (chapterIndex > -1) this.chapters[chapterIndex] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: chapter,
success: p => {
this.computeds.splice(computedIndex, 1)
callback && callback( (chapter.isStart || chapter.isEnd) ? 'end' : status)//关闭加载动画
const pages = isPrev ? p.concat(this.pages) : this.pages.concat(p)//添加新计算的章节内容
this.pages = pages//渲染章节内容
if ( isPrev ) {//如果是加载上一章节需要重新定位
this.$nextTick(function () {
this.current = pages.findIndex(page => page.index == nowIndex)//定位页面
this.$refs.scroll.scrollToIndex(this.current)//刷新翻页组件
})
}
this.chapterLoading = false//关闭章节加载等待
}
})
}, 100)
})
} else {
this.chapterLoading = false//关闭章节加载等待
callback && callback(status)//关闭加载动画
}
}
}
}
\ No newline at end of file
<template>
<view class="yingbing-content">
<text space="nbsp" :selectable="selectable" :user-select="selectable" :style="[contentStyle]" v-for="(t, i) in item.contents" :key="i">{{t}}</text>
</view>
</template>
<script>
export default {
inject: ['getColor', 'getFontSize', 'getFontFamily', 'getLineGap', 'getSelectable'],
props: {
item: {
type: Object,
default () {
return new Object
}
}
},
computed: {
color () {
return this.getColor()
},
fontSize () {
return this.getFontSize()
},
fontFamily () {
return this.getFontFamily()
},
lineGap () {
return this.getLineGap()
},
selectable () {
return this.getSelectable()
},
contentStyle () {
return {
'color': this.color,
'font-size': this.fontSize + 'px',
'font-family': this.fontFamily,
'margin-top': this.lineGap + 'px',
'height': this.fontSize + 'px'
}
}
}
}
</script>
<style scoped>
.yingbing-content {
box-sizing: border-box;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
</style>
\ No newline at end of file
export default {
methods: {
//判断是否显示头部
getFlipHeaderShow (item) {
return ['text', 'slot'].includes(item.type) ? typeof item.headerShow == 'boolean' ? item.headerShow : this.headerShow : false
},
//判断是否显示底部
getFlipFooterShow (item) {
return ['text', 'slot'].includes(item.type) ? typeof item.footerShow == 'boolean' ? item.footerShow : this.footerShow : false
},
//渲染页面
flipRender ({chapter, current, start = 0}) {
if ( chapter ) {//如果传入章节内容
const index = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (index > -1) this.chapters[index] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
}
current = parseInt(current || 0)//强制转换类型为int
start = parseInt(start)//强制转换类型为int
this.pages = [{index: current, type: 'loading'}]//显示loading
this.current = 0//重置current
this.$refs.flip.refresh()
const cgs = this.chapters.filter(c => c.index == current || c.index == current - 1 || c.index == current + 1)//筛选出符合条件的三个章节内容
let index = 0, arr = []
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
const handle = () => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: cgs[index],
success: pages => {
arr = arr.concat(pages)
if ( index < cgs.length - 1 ) {
index++
this.$nextTick(function () { handle() })
} else {
this.computeds.splice(computedIndex, 1)
this.pages = this.handlePages(arr)
const pageIndex = this.pages.findIndex(p => start >= p.start && start < p.end && p.index == current )//定位章节
if ( pageIndex == -1 ) this.current = this.pages.findIndex(p => start >= p.end && p.index == current )//定位章节
else this.current = pageIndex
this.$nextTick(function () {
this.$refs.flip.refresh()
this.handleChange({current: this.current, detail: this.pages[this.current] })
this.autoplaySync = this.autoplay
})
}
}
})
}
handle()
}, 100)
})
},
//渲染下一章节
handleFlipLoadRender (status, chapter, callback, isPrev) {
if (status == 'success') {//获取内容成功
const nowIndex = isPrev ? this.pages[0].index : this.pages[this.pages.length-1].index
const chapterIndex = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (chapterIndex > -1) this.chapters[chapterIndex] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: chapter,
success: p => {
this.computeds.splice(computedIndex, 1)
const pages = isPrev ? p.concat(this.pages) : this.pages.concat(p)//添加新计算的章节内容
this.pages = pages//渲染章节内容
if ( isPrev ) {//如果是加载上一章节需要重新定位
this.current = pages.findIndex(page => page.index == nowIndex)//定位页面
this.$refs.flip.refresh()//刷新翻页组件
}
this.chapterLoading = false//关闭章节加载等待
callback && callback(status)//关闭加载动画
this.handleChange({current: this.current, detail: this.pages[this.current]})//触发change事件
}
})
}, 100)
})
} else {
this.chapterLoading = false//关闭章节加载等待
callback && callback(status)//关闭加载动画
}
}
}
}
\ No newline at end of file
export default {
computed: {
currentProgress () {
return this.pages[this.current]?.progress || 0
},
currentTitle () {
return this.pages[this.current]?.title || this.title
},
currentTotal () {
return this.pages[this.current]?.total || 0
},
currentPage () {
return this.pages[this.current]?.current || 0
}
},
methods: {
//渲染页面
scrollRender ({chapter, current, start = 0}) {
if ( chapter ) {//如果传入章节内容
const index = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (index > -1) this.chapters[index] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
}
current = parseInt(current || 0)//强制转换类型为int
start = parseInt(start)//强制转换类型为int
this.pages = [{index: current, type: 'loading'}]//显示loading
this.current = 0//重置current
const cgs = this.chapters.filter(c => c.index == current || c.index == current - 1 || c.index == current + 1)//筛选出符合条件的三个章节内容
let index = 0, arr = []
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
const handle = () => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: cgs[index],
success: pages => {
arr = arr.concat(pages)
if ( index < cgs.length - 1 ) {
index++
this.$nextTick(function () { handle() })
} else {
this.computeds.splice(computedIndex, 1)
this.pages = this.handlePages(arr)
const pageIndex = this.pages.findIndex(p => start >= p.start && start < p.end && p.index == current )//定位章节
if ( pageIndex == -1 ) this.current = this.pages.findIndex(p => start >= p.end && p.index == current )//定位章节
else this.current = pageIndex
this.$nextTick(function () {
this.$refs.scroll.resetRefresh()
this.$refs.scroll.scrollToIndex(this.current)
this.handleChange({current: this.current, detail: this.pages[this.current] })
this.$nextTick(function () {
this.autoplaySync = this.autoplay
})
})
}
}
})
}
handle()
}, 100)
})
},
//翻页改变事件
async handleScroll (e) {
const scrollTop = e.detail.scrollTop
const scrollHeight = e.detail.scrollHeight
if ( !this.scrolling ) {
try{
this.scrolling = true
const rate = Math.floor(scrollTop / this.contentHeight)
let maybe = this.pages[rate] ? rate : this.pages.length-1
let top = -1
while ( top < 0 && maybe < this.pages.length - 1 ) {
const itemRect = await this.$refs.scroll.getItemRect(maybe)
top = itemRect.top
top < 0 ? maybe++ : null
}
const current = top >= 0 ? maybe : this.pages.length - 1
if ( current != this.current ) {
this.handleChange({current: current, detail: this.pages[current]})
this.$refs.footer && this.$refs.footer.refresh()
}
this.scrolling = false
}catch(e){
this.scrolling = false
}
}
},
//渲染下一章节
handleScrollLoadRender (status, chapter, callback, isPrev) {
if ( status == 'success' ) {//获取内容成功
const nowIndex = isPrev ? this.pages[0].index : this.pages[this.pages.length-1].index
const chapterIndex = this.chapters.findIndex(c => c.index == chapter.index)//是否已经包含相同章节
if (chapterIndex > -1) this.chapters[chapterIndex] = chapter//如果包含则更新
else this.chapters.push(chapter)//否则添加新章节
const computedId = this.getComputedId()
this.computeds.push(computedId)
const computedIndex = this.computeds.indexOf(computedId)
this.$nextTick(function () {
setTimeout(() => {
this.$refs.computed[computedIndex] && this.$refs.computed[computedIndex].start({
chapter: chapter,
success: p => {
this.computeds.splice(computedIndex, 1)
callback && callback( (chapter.isStart || chapter.isEnd) ? 'end' : status)//关闭加载动画
const pages = isPrev ? p.concat(this.pages) : this.pages.concat(p)//添加新计算的章节内容
this.pages = pages//渲染章节内容
if ( isPrev ) {//如果是加载上一章节需要重新定位
this.$nextTick(function () {
this.current = pages.findIndex(page => page.index == nowIndex)//定位页面
this.$refs.scroll.scrollToIndex(this.current)//刷新翻页组件
})
}
this.chapterLoading = false//关闭章节加载等待
}
})
}, 100)
})
} else {
this.chapterLoading = false//关闭章节加载等待
callback && callback(status)//关闭加载动画
}
}
}
}
\ No newline at end of file
/deep/ .whole-reader-wrapper {
width: 100%;
height: 100%;
box-sizing: border-box;
position: relative;
z-index: 0;
}
/deep/ .whole-reader-wrapper-selectable {
-webkit-user-select: text; /*webkit浏览器*/
-khtml-user-select: text; /*早期浏览器*/
-moz-user-select: text; /*火狐*/
-ms-user-select: text; /*IE10*/
user-select: text;
}
/* 头部元素 */
/deep/ .whole-reader-header {
display: flex;
align-items: center;
height: 20px;
margin-bottom: 10px;
}
/deep/ .whole-reader-header-back {
width: 6px;
height: 6px;
border-top-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-left-style: solid;
transform: rotateZ(-45deg);
margin-right: 5px;
opacity: 0.5;
}
/deep/ .whole-reader-header-text {
font-size: 10px;
opacity: 0.5;
}
/* 底部元素 */
/deep/ .whole-reader-footer {
display: flex;
align-items: center;
justify-content: space-between;
height: 20px;
margin-top: 10px;
}
/deep/ .whole-reader-footer-left {
display: flex;
align-items: center;
}
/deep/ .whole-reader-footer-text {
font-size: 10px;
opacity: 0.5;
margin-left: 10px;
}
/* 电池元素 */
/deep/ .whole-reader-battery {
display: flex;
align-items: center;
opacity: 0.5;
}
/deep/ .whole-reader-battery-wrapper {
width: 20px;
height: 9px;
border-width: 1px;
border-style: solid;
padding: 1px;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
}
/deep/ .whole-reader-battery-content {
flex: 1;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
}
/deep/ .whole-reader-battery-content-value {
height: 5px;
}
/deep/ .whole-reader-battery-content-value-text {
font-size: 8px;
}
/deep/ .whole-reader-battery-top {
width: 2px;
height: 5px;
}
/* 滚动阅读 */
/deep/ .whole-scroll-reader {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
box-sizing: border-box;
}
/deep/ .whole-scroll-reader-content {
flex: 1;
overflow-y: auto;
}
/deep/ .whole-scroll-reader-content::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
}
/* 翻页阅读模式 */
/deep/ .whole-flip-reader {
overflow: hidden;
}
/deep/ .whole-flip-reader-page-item {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
/deep/ .whole-flip-reader-page-item-content {
position: absolute;
display: flex;
flex-direction: column;
top: 0;
left: 0;
overflow: hidden;
box-sizing: border-box;
}
/deep/ .whole-flip-reader-content-text {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
}
/deep/ .whole-flip-reader-page-item-bg {
position: absolute;
top: 50%;
left: 100%;
transform: translateY('-50%');
width: 100%;
box-shadow: 10px 0 20px rgba(0,0,0,0.2);
}
/deep/ .whole-flip-reader-page-item-shadow {
width: 0;
height: 100%;
position: absolute;
top: 0;
right: 0;
z-index: 9;
}
\ No newline at end of file
This diff is collapsed.
<html>
<head>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
/>
<title>富文本展示</title>
<style type="text/css">
html,body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
box-sizing: border-box;
}
.yingbing-content {
color: #333;
width: 100%;
height: 100%;
}
.yingbing-img {
max-width: 100%!important;
max-height: 100%!important;
}
</style>
</head>
<body>
<div class="yingbing-content"></div>
</body>
<script type="text/javascript">
window.onload = function () {
var search = window.location.search.slice(1);
var arr = search.split('&');
for ( var i = 0; i < arr.length; i++ ) {
var strs = arr[i].split('=');
if ( strs[0] == 'content' ) {
document.querySelector('.yingbing-content').innerHTML = decodeURIComponent(strs[1])
}
}
}
function setFontFace (fontList) {
let code = fontList.reduce((accumulator, currentValue) => {
return accumulator + `@font-face { font-family: ${currentValue.fontFamily};src: url('${currentValue.src}'); }`;
}, "");
var style = document.createElement("style");
style.type = "text/css";
style.rel = "stylesheet";
style.appendChild(document.createTextNode(code));
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
</script>
</html>
\ No newline at end of file
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function i(e,i){return n.call(e,i)}var t=[];function r(){return window.__dcloud_weex_postMessage||window.__dcloud_weex_}var o=function(e,n){var i={options:{timestamp:+new Date},name:e,arg:n};if(r()){if("postMessage"===e){var o={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(o):window.__dcloud_weex_.postMessage(JSON.stringify(o))}var a={type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(a):window.__dcloud_weex_.postMessageToService(JSON.stringify(a))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:i,pageId:""},"*");if(0===t.length){var d=plus.webview.currentWebview();if(!d)throw new Error("plus.webview.currentWebview() is undefined");var s=d.parent(),w="";w=s?s.id:d.id,t.push(w)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}},"__uniapp__service");else{var u=JSON.stringify(i);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(u,",").concat(JSON.stringify(t),");"))}},a={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;o("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;o("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;o("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;o("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;o("redirectTo",{url:encodeURI(n)})},getEnv:function(e){r()?e({nvue:!0}):window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};o("postMessage",e.data||{})}},d=/uni-app/i.test(navigator.userAgent),s=/Html5Plus/i.test(navigator.userAgent),w=/complete|loaded|interactive/;var u=window.my&&navigator.userAgent.indexOf(["t","n","e","i","l","C","y","a","p","i","l","A"].reverse().join(""))>-1;var g=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var v=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var c=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var m=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);var f=window.ks&&window.ks.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var l=window.tt&&window.tt.miniProgram&&/Lark|Feishu/i.test(navigator.userAgent);var _=window.jd&&window.jd.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var E=window.xhs&&window.xhs.miniProgram&&/xhsminiapp/i.test(navigator.userAgent);for(var h,P=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},b=[function(e){if(d||s)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&w.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),a},function(e){if(m)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(v)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(u){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(c)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(f)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.ks.miniProgram},function(e){if(l)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(_)return window.JDJSBridgeReady&&window.JDJSBridgeReady.invoke?setTimeout(e,0):document.addEventListener("JDJSBridgeReady",e),window.jd.miniProgram},function(e){if(E)return window.xhs.miniProgram},function(e){return document.addEventListener("DOMContentLoaded",e),a}],y=0;y<b.length&&!(h=b[y](P));y++);h||(h={});var B="undefined"!=typeof uni?uni:{};if(!B.navigateTo)for(var S in h)i(h,S)&&(B[S]=h[S]);return B.webView=h,B}));
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
## 1.0.9(2024-05-27)
* 增加autoplay和interval属性控制自动播放
## 1.0.8(2024-03-29)
* 修复app-vue初始化报错,并且无法翻页的问题
## 1.0.7(2024-03-29)
* 恢复微信小程序使用插槽的方式
* 保留微信小程序引入子组件的方式
## 1.0.6(2024-03-06)
* 添加vue3兼容
* 修改微信小程序使用方式(为了提高性能以及兼容vue3)如果使用过该组件编写微信小程序的开发者,没有什么问题可以不用更新,避免出错
## 1.0.5(2023-11-13)
* 增加pc端兼容
## 1.0.4(2023-09-02)
* 解决插槽中有scroll-view等滚动组件时滑动报错的问题
## 1.0.3(2023-08-23)
* 解决APP-NVUE端resetLoading不生效的问题
## 1.0.2(2023-07-25)
* 解决竖直翻页时,点击上下2侧翻页不准确得问题
## 1.0.1(2023-07-24)
* 解决APP-NVUE无法点击左右2侧翻页得问题
## 1.0.0(2023-07-22)
* 第一次更新
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment