优化阅读页面

This commit is contained in:
lipnggao 2023-09-21 14:49:07 +08:00
parent 2c18c9ec0d
commit 184e85da84
24 changed files with 6549 additions and 3969 deletions

View File

@ -12,12 +12,42 @@
:line-height="defaultCharactersLineHeight" :color="novelContentColor" :bg-color="bodyReadingBg"
:slide="20" :enablePreload="false" @loadmore="loadmoreContent" @preload="preloadContent"
@change="currentChange" @setCatalog="setCatalog" @clickme="clickme" @clickher="clickher"
@clickTo="handelShowStepUp" :clickOption="{width:200,height: 400,left:'auto',top:'auto'}">
@clickTo="handelShowStepUp" :clickOption="{width:200,height: 400,left:'auto',top:'auto'}"
@handelPurchaseFull="handelPurchaseFull">
<template #test>
<view class="balance_con">
<rich-text class="balance_con_rich_text"
:style="`color:${novelContentColor};font-size:${newCharactersSize}px;`"
:nodes="readChapterInfoObj.content"></rich-text>
<view class="balance_recharge_option"></view>
<view class="balance_recharge" :style="`background:${bodyReadingBg}`">
<view class="balance_tips" :style="`color:${novelContentColor}`">
付费章节需要购买{{readChapterInfoObj.price}}书币</view>
<view class="balance_btn_all">
<view v-if="!token" class="purchaseFull_popup_btn" @click="toPathLogin">新用户登录</view>
<view v-else class="purchaseFull_popup_btn" @click="handelPurchaseFull">需要全本购买
</view>
</view>
</view>
</view>
</template>
</yingbing-ReadPage>
</view>
</view>
<view class="u_popup_all">
<u-popup :show="tabBarPopupShow" mode="bottom" :overlay="false" zIndex="6" :bgColor="bodyReadingBg">
<u-popup :show="readingPopupshow" mode="bottom" :overlay="false" zIndex="4" bgColor="transparent">
<view class="reading_schedule_box" :style="`background:${bodyReadingBg}`">
<view class="reading_schedule_body">
<view class="_previous_chapter"
:style="`color:${previousChapterBbuttonTextColor};background:${previousChapterBbuttonBg}`"
@tap="previousChapter">
上一章
</view>
<view class="_next_chapter"
:style="`color:${nextChapterBbuttonTextColor};background:${nextChapterBbuttonBg}`"
@tap="nextChapter">下一章
</view>
</view>
<view class="my_tabBar_Reading" :style="`background:${bodyReadingBg}`">
<view class="tabBar_Reading_item">
<view class="reading_item_icon" @tap="handelDirectoryPopup">
@ -38,21 +68,6 @@
<view class="reading_item_name" :style="`color:${tabBarTextColor}`">设置</view>
</view>
</view>
</u-popup>
<u-popup :show="readingPopupshow" mode="bottom" :overlay="false" zIndex="4" bgColor="transparent">
<view class="reading_schedule_box" :style="`background:${bodyReadingBg}`">
<view class="reading_schedule_body">
<view class="_previous_chapter"
:style="`color:${previousChapterBbuttonTextColor};background:${previousChapterBbuttonBg}`"
@tap="previousChapter">
上一章
</view>
<view class="_next_chapter"
:style="`color:${nextChapterBbuttonTextColor};background:${nextChapterBbuttonBg}`"
@tap="nextChapter">下一章
</view>
</view>
<view style="height: 140rpx" />
</view>
</u-popup>
<u-popup :show="stepUpPopupShow" mode="bottom" :overlay="false" bgColor="transparent" zIndex="5">
@ -103,7 +118,7 @@
</scroll-view>
</view>
</u-popup>
<u-popup :show="purchaseFullShow" @close="purchaseFullClose" mode="bottom" overlayStyle="top:44px">
<!-- <u-popup :show="purchaseFullShow" @close="purchaseFullClose" mode="bottom" overlayStyle="top:44px">
<view class="purchaseFull_popup_box" :style="`background:${bodyReadingBg}`">
<view>
<CommBookLeftRigth :bookTips="bookInfo.category_name" :bookName="bookInfo.title"
@ -113,9 +128,9 @@
<view class="purchaseFull_popup_btn" @tap="handelPurchaseFull">需要全本购买</view>
</view>
</view>
</u-popup>
<u-modal :show="balanceShow" :title="balanceTitle" content='余额不足,请充值...'
:showCancelButton="!readChapterFlag" @confirm="balanceConfirm" @cancel="balanceCancel" />
</u-popup> -->
<!-- <u-modal :show="balanceShow" :title="balanceTitle" content='余额不足,请充值...'
:showCancelButton="!readChapterFlag" @confirm="balanceConfirm" @cancel="balanceCancel" /> -->
</view>
</view>
</template>
@ -149,6 +164,7 @@
},
data() {
return {
token: '',
navbarTitle: '小说阅读页面',
novelMainTypeColor: '',
setUpColorAll: {},
@ -165,7 +181,6 @@
bodyReadingBg: '',
//
// bottomSecureHeight: 0,
tabBarPopupShow: false,
readingPopupshow: false,
stepUpPopupShow: false,
purchaseFullShow: false,
@ -189,7 +204,7 @@
directoryPopupShow: false,
//
novelReadingContentText: [],
charactersPageType: 'real',
charactersPageType: 'reals',
defaultCharactersSize: 28,
newCharactersSize: 28,
defaultCharactersLineHeight: 20,
@ -211,23 +226,25 @@
this.readChapterid = options.id;
},
onShow() {
const token = myGetStorage('token');
this.token = token;
const bookSid = this.booksDirectorySid;
const data = {
sid: bookSid,
}
uni.$u.http.post('/bookdetails', data).then((res) => {
uni.hideLoading();
if (res.status == 1) {
const info = res.data.info;
const cover = info.cover.includes('http') ? info.cover : `${config.baseUrl}${info.cover}`;
this.bookInfo = {
...info,
cover
};
}
}).catch((err) => {
uni.hideLoading();
})
// const data = {
// sid: bookSid,
// }
// uni.$u.http.post('/bookdetails', data).then((res) => {
// uni.hideLoading();
// if (res.status == 1) {
// const info = res.data.info;
// const cover = info.cover.includes('http') ? info.cover : `${config.baseUrl}${info.cover}`;
// this.bookInfo = {
// ...info,
// cover
// };
// }
// }).catch((err) => {
// uni.hideLoading();
// })
this.isGetUserRead();
},
methods: {
@ -277,23 +294,23 @@
title: resData.chaptername,
isTtitle: resData.title
// richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
// richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
};
this.readChapterInfoObj = readChapterInfoObj;
if (readChapterInfoObj.chackpay == 1) {
resolve(readChapterInfoObj);
} else if (readChapterInfoObj.chackpay == 2) {
this.balanceShow = true;
this.balanceTitle = '下一章,付费章节';
if (isShowLoading) {
this.readChapterFlag = true;
}
} else if (readChapterInfoObj.chackpay == 3) {
this.purchaseFullShow = true;
if (isShowLoading) {
this.readChapterFlag = true;
}
}
// if (readChapterInfoObj.chackpay == 1) {
// } else if (readChapterInfoObj.chackpay == 2) {
// this.balanceShow = true;
// this.balanceTitle = '';
// if (isShowLoading) {
// this.readChapterFlag = true;
// }
// } else if (readChapterInfoObj.chackpay == 3) {
// this.purchaseFullShow = true;
// if (isShowLoading) {
// this.readChapterFlag = true;
// }
// }
}
}).catch((err) => {
uni.hideLoading();
@ -303,35 +320,65 @@
},
async loadmoreContent(chapter, callback) {
console.log(chapter, "*****************")
const newReadDirectoryActive = this.readDirectoryActive;
const newReadChapterLastid = this.readChapterLastid;
const newNovelReadingContentText = this.novelReadingContentText;
const newReadChapterNextid = this.readChapterNextid;
console.log(newNovelReadingContentText,"newNovelReadingContentText")
// const itemTemp = newNovelReadingContentText.filter((m) => m.chapter == parseInt(chapter + 1));
// console.log(itemTemp,itemTemp[0].lastid, "itemTempitemTempitemTemp")
if (newReadDirectoryActive != 1) {
console.log(chapter, newReadDirectoryActive, "loadmoreContent")
if (chapter != 1) {
if (chapter < newReadDirectoryActive) {
// const readChapterInfoObj = await this.isGetBookInfo(itemTemp[0].lastid);
const readChapterInfoObj = await this.isGetBookInfo(newReadChapterLastid);
console.log(readChapterInfoObj, "上一章")
this.novelReadingContentText = [...newNovelReadingContentText, readChapterInfoObj];
this.readDirectoryActive = chapter;
// this.readChapterLastid = readChapterInfoObj.lastid;
// this.readChapterNextid = readChapterInfoObj.nextid;
if (readChapterInfoObj.chackpay == 1) {
callback('success', readChapterInfoObj)
}
const obj = {
...readChapterInfoObj,
custom,
content: ''
}
console.log(obj, "objobj")
callback('success', obj)
}
if (chapter > newReadDirectoryActive) {
// const readChapterInfoObj = await this.isGetBookInfo(itemTemp[0].nextid);
const readChapterInfoObj = await this.isGetBookInfo(newReadChapterNextid);
console.log(readChapterInfoObj, "下一章")
this.novelReadingContentText = [readChapterInfoObj, ...newNovelReadingContentText];
this.readDirectoryActive = chapter;
// this.readChapterLastid = readChapterInfoObj.lastid;
// this.readChapterNextid = readChapterInfoObj.nextid;
if (readChapterInfoObj.chackpay == 1) {
callback('success', readChapterInfoObj)
}
if (readChapterInfoObj.chackpay == 2) {
const novelContentColor = this.novelContentColor;
const newCharactersSize = this.newCharactersSize;
const bodyReadingBg = this.bodyReadingBg;
//
const custom = [`slot:test`]
// const custom = [
// `<div class="balance_con">
// <div class="balance_con_rich_text"
// style="color:${novelContentColor};font-size:${newCharactersSize}px;"
// >${readChapterInfoObj.content}</div>
// <div class="balance_recharge_option"></div>
// <div class="balance_recharge" style="background:${bodyReadingBg}">
// <div class="balance_tips" style="color:${novelContentColor}">
// ${readChapterInfoObj.price}</div>
// <div class="balance_btn_all">
// <div class="purchaseFull_popup_btn" onclick="handelPurchaseFull"></div>
// </div>
// </div>
// </div>`
// ]
const obj = {
...readChapterInfoObj,
custom,
content: ''
}
console.log(obj, "objobj")
callback('success', obj)
}
}
}
},
currentChange(pageInfo) {
@ -341,19 +388,30 @@
this.readChapterLastid = itemTemp[0].lastid;
this.readChapterNextid = itemTemp[0].nextid;
},
previousChapter() {
const readChapterLastid = this.readChapterLastid
if (!readChapterLastid) {
async previousChapter() {
const newReadDirectoryActive = this.readDirectoryActive;
const newReadChapterLastid = this.readChapterLastid;
const newNovelReadingContentText = this.novelReadingContentText;
if (!newReadChapterLastid) {
uni.showToast({
icon: 'none',
title: "已经是第一章了"
})
return
}
this.isGetBookInfo(readChapterLastid, 'previousChapter');
const readChapterInfoObj = await this.isGetBookInfo(newReadChapterLastid);
this.novelReadingContentText = [readChapterInfoObj];
this.$refs.yingbingReadPage.init({
contents: [readChapterInfoObj],
start: 0,
currentChapter: newReadDirectoryActive - 1
})
this.readDirectoryActive = newReadDirectoryActive - 1;
},
nextChapter() {
async nextChapter() {
const newReadDirectoryActive = this.readDirectoryActive;
const readChapterNextid = this.readChapterNextid;
const newNovelReadingContentText = this.novelReadingContentText;
if (!readChapterNextid) {
uni.showToast({
icon: 'none',
@ -361,11 +419,17 @@
})
return
}
this.isGetBookInfo(readChapterNextid, 'nextChapter');
const readChapterInfoObj = await this.isGetBookInfo(readChapterNextid);
this.novelReadingContentText = [readChapterInfoObj];
this.$refs.yingbingReadPage.init({
contents: [readChapterInfoObj],
start: 0,
currentChapter: newReadDirectoryActive + 1
})
this.readDirectoryActive = newReadDirectoryActive + 1;
},
handelDirectoryItem(row) {},
handelShowStepUp() {
this.tabBarPopupShow = !this.tabBarPopupShow;
this.readingPopupshow = !this.readingPopupshow;
this.stepUpPopupShow = false;
},
@ -377,6 +441,13 @@
url: `/pages/bookRecommendList/bookRecommendList?sid=${readChapterInfoObj.sid}&t=${readChapterInfoObj.title}&c=${readDirectoryActive}`
})
},
toPathLogin() {
const readChapterInfoObj = this.readChapterInfoObj;
const readDirectoryActive = this.readDirectoryActive;
uni.navigateTo({
url: `/pages/login/login?sid=${readChapterInfoObj.sid}&t=${readChapterInfoObj.title}&c=${readDirectoryActive}`
})
},
directoryPopupClose() {
// this.directoryPopupShow = false;
// const readChapterInfoObj = this.readChapterInfoObj;
@ -545,30 +616,6 @@
})
}
},
onPullDownRefresh() {
// const isReadDirectoryActive = this.readDirectoryActive;
// if(isReadDirectoryActive == 0) {
// uni.showToast({
// icon:'none',
// title:""
// })
// uni.stopPullDownRefresh()
// return
// }
// const readDirectoryActive = isReadDirectoryActive- 1;
// const novelReadingContentText = this.myData[readDirectoryActive];
// this.novelReadingContentText = [novelReadingContentText, ...this.novelReadingContentText];
// // setTimeout(() => {
// this.computeRichText = this.myData[readDirectoryActive].content;
// // this.readDirectoryActive = readDirectoryActive;
// const query = uni.createSelectorQuery().in(this);
// this.$nextTick(() => {
// query.select(`#compute_rich_text`).boundingClientRect((data) => {
// this.isScrollTop = parseInt(data.height) - 30;
// }).exec();
// });
// uni.stopPullDownRefresh()
},
created() {
const novelMainObj = myGetStorage('novelMainObj') || '{}';
const novelMainTypeColor = JSON.parse(novelMainObj).novelMainTypeColor || 'F3EFE9';
@ -581,17 +628,6 @@
this.novelMainTypeColor = novelMainTypeColor;
this.newCharactersSize = JSON.parse(novelMainObj).charactersSize || 28;
this.bodyReadingHeight = screenHeight - statusBarHeight - devicePixelRatio * 22;
//
// this.scrollReadingHeight = screenHeight - statusBarHeight - devicePixelRatio * 38;
// this.scrollReadingHeight = windowHeight - (screenWidth / 375) * 54;
// // #ifdef APP-PLUS
// this.directoryPopupHeight = screenHeight - statusBarHeight;
// // #endif
// // #ifdef H5 || MP-WEIXIN
// this.directoryPopupHeight = screenHeight - statusBarHeight - devicePixelRatio * 22;
// // #endif
// this.bottomSecureHeight = screenHeight - windowHeight;
this.setUpColorAll = setUpReadingColorAll;
this.barPopupIcon = {
'F3EFE9': {
@ -631,6 +667,96 @@
margin-top: 40rpx;
}
/deep/.balance_con {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.balance_con_rich_text {
width: 100%;
height: 80%;
line-height: 2;
overflow: hidden;
// text-overflow: ellipsis;
// display: -webkit-box;
// -webkit-line-clamp: 10;
// -webkit-box-orient: vertical;
}
.balance_recharge_option {
position: fixed;
bottom: 0;
left: 0;
width: 414px;
height: 560rpx;
// box-shadow: 0 0 8rpx rgba(0, 0, 0, 0.2);
background: rgba(243, 239, 233, 0.7);
filter: blur(10px);
// user-select: none;
// opacity: 0.6;
// background: #fff;
// background-image: linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.6) 80%);
}
// .balance_recharge_option::after {
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// content: '';
// display: block;
// }
.balance_recharge {
// display: flex;
// flex-direction: column;
// justify-content: center;
// align-items: center;
position: fixed;
bottom: 0rpx;
left: 0;
width: 100%;
box-shadow: 0 0 8rpx rgba(0, 0, 0, 0.2);
height: 360rpx;
border-radius: 24rpx 24rpx 0 0;
padding: 32rpx;
box-sizing: border-box;
// background-image: linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, rgb(255, 255, 255) 80%);
// background: rgba(255, 255, 255, 0.2);
// filter: blur(5px);
// user-select: none;
// box-shadow: 0 -20rpx 20rpx rgba(0, 0, 0, 0.2);
.balance_tips {
font-size: 30rpx;
}
.balance_btn_all {
// display: flex;
// justify-content: center;
// align-items: center;
margin-top: 32rpx;
.purchaseFull_popup_btn {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 84rpx;
font-size: 30rpx;
color: #fff;
background: linear-gradient(to top, #FBA676, #E95E32);
border-radius: 24rpx;
}
}
}
}
.novelReading_content::v-deep.u-navbar__content__title {
font-size: 30rpx;
}

View File

@ -1,3 +1,43 @@
## 1.5.92023-09-01
* 修复滚动模式下计算滚动位置异常的问题
## 1.5.82023-08-23
* 修复APP-VUE翻页翻往下一章时可能会出现的undefined
## 1.5.72023-08-21
* 增加split属性用于分隔字符串排版适用于英文小说
* measureSize属性中将英文字母大小拆分成小写英文字母和大写英文字母大小
## 1.5.62023-07-24
* 解决bgColor属性无效的问题
## 1.5.52023-07-22
* 请注意此次更新将翻页功能抽出来,插件本身只有滚动阅读功能,如果需要翻页功能请下载新插件好用翻页组件配合使用,下载地址看使用介绍
* 此次更新未更新新功能,不想多下载插件的朋友可以不更新
## 1.5.42023-07-10
* 增加firstTipUnable、lastTipUnable属性控制第一页提示页和最后一页提示页的显示
## 1.5.32023-07-08
* 自定义插槽添加作用域插槽,可以获取当前页信息,用于动态绑定内容(不支持微信小程序)
## 1.5.22023-07-07
* 增加firstTiplastTip属性自定义第一页和最后一页提示文字
* 增加top、bottom插槽自定义第一页和最后一页的页面
## 1.5.12023-07-07
* 新增unableClickPage属性控制点击左右2侧翻页
## 1.5.02023-06-20
* 修复微信小程序报错的问题
## 1.4.92023-06-14
* 修复向前翻页页面抖动的问题
* 优化loadmore事件返回回调为fail或者timeout时的显示
* 优化change事件不再强制传contents
## 1.4.82023-06-12
* 修复h5APP-VUE自定义页面添加点击事件无法点击的问题
## 1.4.72023-06-07
* 优化h5电量显示避免报错
## 1.4.62023-06-07
* 新增自动翻页功能
* 优化页面刷新逻辑,减少报错
## 1.4.52023-05-27
* 添加字符宽度自动测量功能,减少排版错误
* 新增measureSize属性用于自定义字符宽度
## 1.4.42023-05-26
* 翻页模式添加touchcancel事件
* 为兼容IOS空格计算宽度增加1px
## 1.4.32023-05-25
* 新增fontFamily属性设置字体名称
* 新增fontFace属性添加自定义字体

View File

@ -34,13 +34,14 @@
}
},
// #endif
mounted () {
created () {
this.getBattery()
},
methods: {
getBattery () {
// #ifdef H5
window.navigator.getBattery().then((res) => {
//window.navigator.getBatteryhttps file:///url使
window.navigator.getBattery && window.navigator.getBattery().then((res) => {
// 01100
this.value = res.level * 54
});

View File

@ -0,0 +1,14 @@
.computed {
position: fixed;
top: -1000rpx;
left: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
}
.computed-text {
font-size: 20px;
flex-shrink: 0;
}

View File

@ -1,8 +1,22 @@
import Util from '../../../js_sdk/util.js'
export default {
props: {
measureSize: {
type: Object,
default () {
return new Object
}
}
},
data () {
return {
computedResolve: null
computedResolve: null,
chineseSize: 0,
spaceSize: 0,
lowerSize: 0,
upperSize: 0,
numberSize: 0,
specialSize: 0
}
},
methods: {
@ -49,29 +63,62 @@ export default {
text = new String(text);
text = text.split('');
let width = 0;
text.forEach(function(item) {
if (/[a-zA-Z]/.test(item)) {
width += 7;
text.forEach((item) => {
if (/[a-z]/.test(item)) {
width += this.measureSize.lower || this.lowerSize || 7
} else if ( /[A-Z]/.test(item) ) {
width += this.measureSize.upper || this.upperSize || 7
} else if (/[0-9]/.test(item)) {
width += 5.5;
} else if (/\./.test(item)) {
width += 2.7;
} else if (/-/.test(item)) {
width += 3.25;
width += this.measureSize.number || this.numberSize || 5.5
} else if (/[\u4e00-\u9fa5]/.test(item)) { //中文匹配
width += 10;
} else if (/\(|\)/.test(item)) {
width += 3.73;
width += this.measureSize.chinese || this.chineseSize || 10
} else if (/\s/.test(item)) {
width += 2.5;
width += this.measureSize.space || this.spaceSize || 3.5
} else if (/[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(item)) {
width += 8;
width += this.measureSize.special || this.specialSize || 8
} else {
width += 10;
width += this.measureSize.other || this.chineseSize || 10
}
});
return width * fontSize / 10;
},
getComputedTextSize (selector, el) {
let arr = []
arr.push(Util.getRect('.computed-text-chinese', this.$refs.computedTextChinese, this))
arr.push(Util.getRect('.computed-text-space', this.$refs.computedTextSpace, this))
arr.push(Util.getRect('.computed-text-lower', this.$refs.computedTextLower, this))
arr.push(Util.getRect('.computed-text-upper', this.$refs.computedTextUpper, this))
arr.push(Util.getRect('.computed-text-number', this.$refs.computedTextNumber, this))
arr.push(Util.getRect('.computed-text-special', this.$refs.computedTextSpecial, this))
Promise.all(arr).then(ress => {
ress.forEach((res, key) => {
if ( key == 0 ) {
this.chineseSize = res.width * (10 / 20)
}
if ( key == 1 ) {
this.spaceSize = res.width * (10 / 20)
}
if ( key == 2 ) {
this.lowerSize = res.width * (10 / 20)
}
if ( key == 3 ) {
this.upperSize = res.width * (10 / 20)
}
if ( key == 4 ) {
this.numberSize = res.width * (10 / 20)
}
if ( key == 5 ) {
this.specialSize = res.width * (10 / 20)
}
// console.log('chineseSize', this.chineseSize);
// console.log('spaceSize', this.spaceSize);
// console.log('lowerSize', this.lowerSize);
// console.log('upperSize', this.upperSize);
// console.log('numberSize', this.numberSize);
// console.log('specialSize', this.specialSize);
})
})
},
async computedText (data, start) {
let rect = await this.getRect()
let viewWidth = rect.width - (this.options.slide * 2)
@ -91,32 +138,41 @@ export default {
text: []
}
let length = 0;
let contentSync = data.content.substr(start);
let contentSync = data.content.substr(start).replace(/\t/g, ' ').replace(/ /g, ' ');
let lastIndex = 0;
while ( (pageHeight + this.options.fontSize + this.options.lineHeight) <= viewHeight ) {
strs.push('');
let lineWidth = 0;
let charText = ''
for ( let i = lastIndex; i < contentSync.length; i++ ) {
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
length += 1
const char = contentSync.charAt(i)
if ( JSON.stringify(char) == JSON.stringify('\r') || JSON.stringify(char) == JSON.stringify('\n') ) {
lineWidth += this.measureText(charText, this.options.fontSize);
if ( lineWidth >= viewWidth ) {
lastIndex = i - charText.length+ 1;
break;
}
strs[strs.length - 1] += charText
length += charText.length + 1
page.end = page.start + length;
lastIndex = i + 1;
break;
}
lineWidth += JSON.stringify(contentSync[i]) == JSON.stringify('\t') ? 0 : this.measureText(contentSync[i], this.options.fontSize);
charText += char
if ( !this.split || char == this.split ) {
lineWidth += this.measureText(charText, this.options.fontSize);
if ( lineWidth >= viewWidth ) {
lastIndex = i;
lastIndex = i - charText.length+ 1;
break;
} else {
if ( JSON.stringify(contentSync[i]) != JSON.stringify('\t') ) {
strs[strs.length - 1] += contentSync[i].replace(' ', ' ')
length += 1
page.end = page.start + length
}
strs[strs.length - 1] += charText
length += charText.length
page.end = page.start + length
charText = ''
}
}
pageHeight += this.options.fontSize + this.options.lineHeight;
if ( page.end >= data.content.replace(/\t/g, '').length - 1 ) {
if ( page.end >= data.content.length - 1 ) {
page.isLastPage = true;
break;
}
@ -188,22 +244,48 @@ export default {
arr = arr.concat(pages)
if (i == contents.length - 1) {
if ( this.options.pageType != 'scroll' ) {
if ( !this.firstTipUnable ) {
arr.unshift({
title: contents[0].title || '',
chapter: contents[0].chapter,
type: contents[0].isStart ? 'top' : 'prevLoading',
type: contents[0].isStart ? 'top' : 'loading',
direction: 'prev',
dataId: arr[0].dataId - 1,
start: 0,
end: 0
})
} else if ( !contents[0].isStart ) {
arr.unshift({
title: contents[0].title || '',
chapter: contents[0].chapter,
type: 'loading',
direction: 'prev',
dataId: arr[0].dataId - 1,
start: 0,
end: 0
})
}
if ( !this.lastTipUnable ) {
arr.push({
title: item.title || '',
chapter: item.chapter,
type: item.isEnd ? 'bottom' : 'nextLoading',
type: item.isEnd ? 'bottom' : 'loading',
direction: 'next',
dataId: arr[arr.length - 1].dataId + 1,
start: 0,
end: 0
})
} else if ( !contents[0].isEnd ) {
arr.push({
title: item.title || '',
chapter: item.chapter,
type: 'loading',
direction: 'next',
dataId: arr[arr.length - 1].dataId + 1,
start: 0,
end: 0
})
}
}
this.pages = arr
if ( this.options.pageType == 'scroll' ) {
@ -211,7 +293,8 @@ export default {
}
this.$nextTick(() => {
if ( this.options.pageType != 'scroll' ) {
this.onChange(this.currentDataId);
this.$refs.flip.refresh()
this.handleFlipChange(this.currentDataId);
} else {
setTimeout(() => {
Util.getRect('#scroll-item_' + this.currentDataId, Util.getRefs(this, 'scrollItem_' + this.currentDataId, 0), this).then(rect => {
@ -235,41 +318,69 @@ export default {
computedPage(e) {
this.computedChapter(e.content).then((pages) => {
let arr = [];
let newPages = [];
const pagesSync = e.type == 'prev' ? pages.concat(this.pages) : this.pages.concat(pages);
pagesSync.forEach(item => {
if (arr.indexOf(item.chapter) == -1) arr.push(item.chapter)
})
if (arr.length > 3) {
let reChapter = e.type == 'prev' ? pagesSync[pagesSync.length - 1].chapter : pagesSync[0].chapter;
newPages = pagesSync.filter(item => item.chapter != reChapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'));
} else {
newPages = pagesSync.filter(item => (item.type == 'text' || item.type == 'custom' || item.type == 'slot'));
}
let newPages = pagesSync.filter(item => (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
// pagesSync.forEach(item => {
// if (arr.indexOf(item.chapter) == -1) arr.push(item.chapter)
// })
// if (arr.length > 3) {
// let reChapter = e.type == 'prev' ? pagesSync[pagesSync.length - 1].chapter : pagesSync[0].chapter;
// newPages = pagesSync.filter(item => item.chapter != reChapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'));
// } else {
// newPages = pagesSync.filter(item => (item.type == 'text' || item.type == 'custom' || item.type == 'slot'));
// }
if ( this.options.pageType != 'scroll' ) {
const prevIndex = this.contents.findIndex(content => content.chapter == newPages[0].chapter);
const nextIndex = this.contents.findIndex(content => content.chapter == newPages[newPages.length - 1].chapter);
if ( !this.firstTipUnable && this.contents[prevIndex].isStart ) {
newPages.unshift({
title: this.contents[prevIndex].title || '',
chapter: this.contents[prevIndex].chapter,
type: this.contents[prevIndex].isStart ? 'top' : 'prevLoading',
type: 'top',
direction: 'prev',
dataId: newPages[0].dataId - 1,
start: 0,
end: 0
})
} else {
newPages.unshift({
title: this.contents[prevIndex].title || '',
chapter: this.contents[prevIndex].chapter,
type: 'loading',
direction: 'prev',
dataId: newPages[0].dataId - 1,
start: 0,
end: 0
})
}
if ( !this.lastTipUnable && this.contents[nextIndex].isEnd ) {
newPages.push({
title: this.contents[nextIndex].title || '',
chapter: this.contents[nextIndex].chapter,
type: this.contents[nextIndex].isEnd ? 'bottom' : 'nextLoading',
type: 'bottom',
direction: 'next',
dataId: newPages[newPages.length - 1].dataId + 1,
start: 0,
end: 0
})
} else {
newPages.push({
title: this.contents[nextIndex].title || '',
chapter: this.contents[nextIndex].chapter,
type: 'loading',
direction: 'next',
dataId: newPages[newPages.length - 1].dataId + 1,
start: 0,
end: 0
})
}
this.pages = newPages
const nowIndex = newPages.findIndex(page => page.dataId == this.currentDataId);
if ( nowIndex == -1 ) {
this.currentDataId = e.type == 'next' ? pages[0].dataId : pages[pages.length - 1].dataId;
this.onChange(this.currentDataId)
this.handleFlipChange(this.currentDataId)
} else {
this.startAutoplay()
}
} else {
let dataId = e.type == 'prev' ? this.pages[0].dataId : this.pages[this.pages.length-1].dataId
@ -322,7 +433,7 @@ export default {
filterPage (pageInfo) {
if ( pageInfo && pageInfo.dataId > -1 ) {
const nowChapters = this.pages.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
let currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId)
const currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId)
if ( currentPage > -1 ) {
return (currentPage + 1) + ' / ' + nowChapters.length
} else {
@ -335,32 +446,6 @@ export default {
filterDate () {
let date = new Date()
return Util.zeroize(date.getHours()) + ':' + Util.zeroize(date.getMinutes())
},
//翻往上一页
pagePrev () {
if ( this.options.pageType != 'scroll' ) {
// #ifndef APP-NVUE
this.pagePrevWxs()
// #endif
// #ifdef APP-NVUE
this.pagePrevBinding()
// #endif
} else {
this.scrollPrev()
}
},
//翻往下一页
pageNext () {
if ( this.options.pageType != 'scroll' ) {
// #ifndef APP-NVUE
this.pageNextWxs()
// #endif
// #ifdef APP-NVUE
this.pageNextBinding()
// #endif
} else {
this.scrollNext()
}
},
}
}

View File

@ -1,250 +0,0 @@
const Binding = uni.requireNativePlugin('bindingx')
const animation = uni.requireNativePlugin('animation')
import Util from '../../../js_sdk/util.js'
export default {
data () {
return {
disableTouch: false,
isTouch: false,
flipTouchTime: 0,
interval: false,
direction: ''
}
},
beforeDestroy () {
if ( this.flip_binding ) {
Binding.unbind({
token: this.flip_binding.token,
eventType: 'pan'
})
this.flip_binding = null
}
if ( this.flip_animation_binding ) {
Binding.unbind({
token: this.flip_animation_binding.token,
eventType: 'timing'
})
this.flip_animation_binding = null
}
},
methods: {
//翻往上一页
pagePrevBinding () {
if ( !this.isTouch && !this.disableTouch ) {
this.isTouch = true
this.startX = 0
this.onFilpTouchend()
}
},
//翻往下一页
pageNextBinding () {
if ( !this.isTouch && !this.disableTouch ) {
this.isTouch = true
this.startX = this.viewWidth
this.onFilpTouchend()
}
},
onFilpTouchstart (event) {
if ( this.isTouch || this.disableTouch ) {
return
}
this.isTouch = true
this.flipTouchTime = 0
this.interval = true
this.setInterval()
let touch = event.touches[0]
this.startX = touch.pageX
this.startY = touch.pageY
},
async onFilpTouchmove (event) {
if ( this.isTouch && (this.pageType == 'real' || this.pageType == 'cover') && !this.disableTouch && this.flipTouchTime > 200 ) {
if ( !this.direction ) {
let touch = event.touches[0]
if ( touch.pageX < this.startX ) {
if ( this.nextDataId ) {
this.direction = 'next'
}
} else {
if ( this.prevDataId ) {
this.direction = 'prev'
}
}
}
if ( this.direction ) {
this.disableTouch = true
let currentDataId = this.direction == 'next' ? this.currentDataId : this.prevDataId
let props = [{
element: Util.getEl(this.$refs['flipItem_' + currentDataId][0]),
property: 'transform.translateX',
expression: `${this.direction == 'next' ? ('x > 0 ? 0 : (x < -' + this.viewWidth + ' ? -' + this.viewWidth + ' : x + 0)') : ('x < 0 ? -' + this.viewWidth + ' : (x > ' + this.viewWidth + ' ? 0 : x-' + this.viewWidth + ')')}`
}]
if ( this.pageType == 'real' ) {
props.push({
element: Util.getEl(this.$refs['flipItemWrapper_' + currentDataId][0]),
property: 'transform.translateX',
expression: `${this.direction == 'next' ? ('x > 0 ? 0 : (x < -' + this.viewWidth + ' ? ' + this.viewWidth + ' : 0 - x)') : ('x < 0 ? ' + this.viewWidth + ' : (x > ' + this.viewWidth + ' ? 0 : ' + this.viewWidth + '-x)')}`
})
props.push({
element: Util.getEl(this.$refs['flipItemBg_' + currentDataId][0]),
property: 'transform.translateX',
expression: `${this.direction == 'next' ? ('x > 0 ? 0 : (x < -' + this.viewWidth + ' ? -' + this.viewWidth + ' : x + 0)') : ('x < 0 ? -' + this.viewWidth + ' : (x > ' + this.viewWidth + ' ? 0 : x-' + this.viewWidth + ')')}`
})
let rect = await this.getRect(this.$refs['flipItemBg_' + currentDataId][0])
let height = rect.height / 2;
let maxDeg = height / 5;
props.push({
element: Util.getEl(this.$refs['flipItemBg_' + currentDataId][0]),
property: 'transform.rotateZ',
expression: `${this.direction == 'next' ? 'y/' + maxDeg : '-(y/' + maxDeg + ')'}`
})
props.push({
element: Util.getEl(this.$refs['flipItemShadow_' + currentDataId][0]),
property: 'width',
expression: `${this.direction == 'next' ? 'abs(x) / 2 + 0' : this.viewWidth / 2 + '-abs(x) / 2'}`
})
}
this.flip_binding = Binding.bind({
anchor: Util.getEl(this.$refs.yingbingFlip),
eventType: 'pan',
props: props
}, (e) => {
if ((e.state == 'end' || e.state == 'cancel') && this.flip_binding) {
this.clearInterval()
Binding.unbind({
token: this.flip_binding.token,
eventType: 'pan'
})
this.flip_binding = null
let value = this.direction == 'next' ? 1 : -1;
if (this.flipTouchTime <= 200) {
let duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 1
this.pageAnimation(value, -value * this.viewWidth, duration);
} else {
let duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 1
let deltaX = Binding.getComputedStyle(Util.getEl(this.$refs['flipItem_' + currentDataId][0])).translateX
let late = this.direction == 'next' ? deltaX : this.viewWidth + deltaX
if (Math.abs(late) >= this.viewWidth / 4) {
this.pageAnimation(value, -value * this.viewWidth, duration)
} else {
let value = this.direction == 'next' ? 1 : -1;
this.pageAnimation(value, 0, duration);
}
}
}
})
} else {
this.resetPageBinding()
}
}
},
onFilpTouchend () {
if ( this.isTouch && !this.disableTouch ) {
this.disableTouch = true
this.clearInterval()
if ( this.flipTouchTime <= 200 ) {
if ( !this.direction ) {
if (this.startX > (this.viewWidth / 4) * 3) {
if ( this.nextDataId ) {
this.direction = 'next'
}
}
if (this.startX < (this.viewWidth / 4)) {
if ( this.prevDataId ) {
this.direction = 'prev'
}
}
}
if ( this.direction ) {
let duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 1
let value = this.direction == 'next' ? 1 : -1;
this.pageAnimation(value, -value * this.viewWidth, duration);
} else {
this.resetPageBinding()
}
} else {
this.resetPageBinding()
}
}
},
getRect (el) {
return new Promise(resolve => {
uni.requireNativePlugin('dom').getComponentRect(el, res => {
resolve(res.size)
})
})
},
setInterval () {
this.flipTouchTimer = setTimeout(() => {
this.flipTouchTime += 10
if ( this.interval ) {
this.setInterval()
}
}, 10)
},
clearInterval () {
this.interval = false
if ( this.flipTouchTimer ) {
clearTimeout(this.flipTouchTimer)
this.flipTouchTimer = null
}
},
pageAnimation (value, offset, duration) {
let currentDataId = this.direction == 'next' ? this.currentDataId : this.prevDataId
let late = this.direction == 'next' ? offset : offset - this.viewWidth;
let flipItemTrans = Binding.getComputedStyle(Util.getEl(this.$refs['flipItem_' + currentDataId][0])).translateX
let props = [{
element: Util.getEl(this.$refs['flipItem_' + currentDataId][0]),
property: 'transform.translateX',
expression: `linear(t, ${flipItemTrans}, ${late - flipItemTrans}, ${duration})`
}]
if ( this.pageType == 'real' ) {
let flipItemWrapperTrans = Binding.getComputedStyle(Util.getEl(this.$refs['flipItemWrapper_' + currentDataId][0])).translateX
props.push({
element: Util.getEl(this.$refs['flipItemWrapper_' + currentDataId][0]),
property: 'transform.translateX',
expression: `linear(t, ${flipItemWrapperTrans}, ${-late - flipItemWrapperTrans}, ${duration})`
})
let flipItemBgTrans = Binding.getComputedStyle(Util.getEl(this.$refs['flipItemBg_' + currentDataId][0])).translateX
props.push({
element: Util.getEl(this.$refs['flipItemBg_' + currentDataId][0]),
property: 'transform.translateX',
expression: `linear(t, ${flipItemBgTrans}, ${late - flipItemBgTrans}, ${duration})`
})
let flipItemShadowWidth = this.flipTouchTime <= 200 && this.direction == 'prev' ? this.viewWidth : Binding.getComputedStyle(Util.getEl(this.$refs['flipItemShadow_' + currentDataId][0])).width
props.push({
element: Util.getEl(this.$refs['flipItemShadow_' + currentDataId][0]),
property: 'width',
expression: `linear(t, ${flipItemShadowWidth}, ${-late - flipItemShadowWidth}, ${duration})`
})
}
this.flip_animation_binding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flip_animation_binding && e.t > duration) {
Binding.unbind({
token: this.flip_animation_binding.token,
eventType: 'timing'
})
this.flip_animation_binding = null
if ( Math.abs(offset) > 0 ) {
this.onChange(value > 0 ? this.nextDataId : this.prevDataId)
}
this.resetPageBinding();
}
})
},
resetPageBinding () {
this.direction = ''
this.flipTouchTime = 0
this.startX = 0
this.startY = 0
this.$nextTick(function () {
this.isTouch = false
this.disableTouch = false
})
}
}
}

View File

@ -5,44 +5,17 @@
right: 0;
bottom: 0;
}
.yingbing-flip {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.flip-item {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.flip-item-wrapper {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
box-sizing: border-box;
/* #endif */
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
bottom: 0;
}
.flip-item-header {
/* #ifndef APP-NVUE */
@ -55,7 +28,7 @@
overflow: hidden;
}
.flip-item-header-text {
font-size: 30rpx;
font-size: 24rpx;
opacity: .4;
font-weight: bold;
/* #ifdef APP-NVUE */
@ -88,13 +61,13 @@
font-weight: bold;
}
.flip-item-content {
flex: 1;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
flex: 1;
}
.flip-item-text {
/* #ifndef APP-NVUE */
@ -115,20 +88,11 @@
align-items: center;
justify-content: center;
}
.flip-item-bg {
position: absolute;
/* #ifdef APP-NVUE */
box-shadow: 0 20rpx 20rpx rgba(0,0,0,0.2);
/* #endif */
.flip-loading-text {
font-size: 20px;
}
.flip-item-shadow {
position: absolute;
width: 0;
top: 0;
bottom: 0;
right: 0;
/* #ifdef APP-NVUE */
background-image: linear-gradient(to right, rgba(255,255,255, 0), rgba(0,0,0,.5));
opacity: 0.5;
/* #endif */
/* #ifndef APP-NVUE */
view, image, input, scroll-view, swiper, swiper-item, text, textarea, video {
position: static;
}
/* #endif */

View File

@ -1,33 +1,14 @@
import Util from '../../../js_sdk/util.js'
// #ifdef APP-NVUE
import FlipBindingx from './bindingx.js'
// #endif
export default {
// #ifdef APP-NVUE
mixins: [FlipBindingx],
// #endif
computed: {
dataReverse () {
let data = JSON.parse(JSON.stringify(this.pages))
return data.reverse()
},
current () {
return this.dataReverse.findIndex(item => item.dataId == this.currentDataId)
return this.pages.findIndex(item => item.dataId == this.currentDataId)
},
prevDataId () {
return this.dataReverse[this.current + 1] && this.dataReverse[this.current + 1].dataId
return this.pages[this.current - 1] && this.pages[this.current - 1].dataId
},
nextDataId () {
return this.dataReverse[this.current - 1] && this.dataReverse[this.current - 1].dataId
},
flipProp () {
return {
prevDataId: this.prevDataId,
nextDataId: this.nextDataId,
currentDataId: this.currentDataId,
pageType: this.options.pageType,
pageTo: this.pageTo
}
return this.pages[this.current + 1] && this.pages[this.current + 1].dataId
}
},
data() {
@ -35,52 +16,27 @@ export default {
currentDataId: -1,
isShow: false,
viewWidth: 0,
viewHeight: 0,
pageTo: 0,
moreLoading: false,
initLoading: true,
loadingText: '正在加载内容',
loadStatus: 'none',
loadChapter: -1,
loadValue: 0
}
},
mounted () {
if ( this.pageType != 'scroll' ) {
this.$nextTick(function () {
setTimeout(() => {
this.getViewRect()
}, 50)
})
moreLoading: false
}
},
methods: {
//翻往上一页
pagePrevWxs () {
this.pageTo = 0
this.$nextTick(function(){
this.pageTo = -1
})
pagePrevFlip () {
this.$refs.flip.flipToPrev()
},
//翻往下一页
pageNextWxs () {
this.pageTo = 0
this.$nextTick(function(){
this.pageTo = 1
})
pageNextFlip () {
this.$refs.flip.flipToNext()
},
reload () {
if ( this.loadStatus == 'fail' || this.loadStatus == 'timeout' ) {
this.initLoading = false
this.loadingText = '正在加载内容'
this.loadStatus = 'none';
this.loadmore(this.loadChapter, this.loadValue);
this.loadChapter = -1;
this.loadValue = 0;
}
reloadLoadmoreFlip (p) {
let loadIndex = this.pages.findIndex(page => p.dataId == page.dataId)
this.$set(this.pages[loadIndex], 'type', 'loading')
let nextChapter = p.direction == 'next' ? p.chapter + 1 : p.chapter - 1
this.loadmoreFlip(nextChapter, p.direction == 'next' ? 1 : -1);
},
loadmore (chapter, value) {
loadmoreFlip (chapter, value) {
this.$emit('loadmore', chapter, (status, content) => {
this.moreLoading = false;
if (status == 'success') {
const index = this.contents.findIndex(item => item.chapter == content.chapter)
if (index > -1) {
@ -93,33 +49,16 @@ export default {
type: value > 0 ? 'next' : 'prev'
});
this.preload(chapter)
this.moreLoading = false;
} else if ( status == 'fail' ) {
this.loadStatus = status;
this.loadingText = '请求失败,点击重试'
this.initLoading = true
this.loadChapter = chapter;
this.loadValue = value;
} else {
this.loadStatus = status;
this.loadingText = '请求超时,点击重试'
this.initLoading = true
this.loadChapter = chapter;
this.loadValue = value;
let loadIndex = this.pages.findIndex(page => page.type == 'loading' && page.direction == (value > 0 ? 'next' : 'prev'))
this.$set(this.pages[loadIndex], 'type', status)
}
})
},
getViewRect () {
return new Promise(resolve => {
Util.getRect('.yingbing-flip', this.$refs.yingbingFlip, this).then(res => {
this.viewWidth = res.width
this.viewHeight = res.height
this.isShow = true
resolve(true)
})
})
handleFlipChangeRender (e) {
this.handleFlipChange(e.detail.dataId)
},
onChange(dataId) {
handleFlipChange (dataId) {
const value = dataId < this.currentDataId ? -1 : 1
this.currentDataId = dataId
const index = this.pages.findIndex(page => page.dataId == dataId);
@ -130,10 +69,9 @@ export default {
pageInfo.currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId) + 1
if ( this.contents[contentIndex].title ) pageInfo.title = this.contents[contentIndex].title
this.pageInfo = pageInfo
this.$emit('change', pageInfo, this.pages)
this._emitPageInfo(pageInfo, this.pages)
const nextType = this.pages[index + value] && this.pages[index + value].type
const loadings = ['nextLoading', 'prevLoading']
if ( loadings.indexOf(this.pages[index].type) >-1 || loadings.indexOf(nextType) >-1) {
if ( this.pages[index].type == 'loading' || nextType == 'loading') {
if (this.moreLoading) return
this.moreLoading = true;
const loadChapter = this.pages[index].chapter + value;
@ -146,8 +84,10 @@ export default {
this.preload(loadChapter)
this.moreLoading = false;
} else {
this.loadmore(loadChapter, value)
this.loadmoreFlip(loadChapter, value)
}
} else {
this.startAutoplay()
}
}
}

View File

@ -1,569 +0,0 @@
<template>
<view
class="yingbing-flip"
ref="yingbingFlip"
:style="{
'background-color': options.bgColor
}"
:prop="flipProp"
:change:prop="flip.propWatcher"
@touchstart="flip.touchstart"
@touchmove="flip.touchmove"
@touchend="flip.touchend">
<template v-if="isShow">
<view v-if="(item.dataId == currentDataIdSync || item.dataId == prevDataId || item.dataId == nextDataId)"
class="flip-item"
:class="'flip-item_' + item.dataId"
v-for="(item, index) in dataReverse"
:key="item.dataId"
:style="{
'transform': item.dataId < currentDataIdSync ? `translateX(${-viewWidth}px)` : ''
}">
<view
class="flip-item-wrapper"
:class="'flip-item-wrapper_' + item.dataId"
:style="{
'padding-left': options.slide + 'px',
'padding-right': options.slide + 'px',
'padding-top': options.topGap + 'px',
'padding-bottom': options.bottomGap + 'px',
'background': options.bgColor,
'transform': item.dataId < currentDataIdSync ? options.pageType == 'real' ? `translateX(${viewWidth}px)` : 'translateX(0)' : ''
}">
<view class="flip-item-header" v-if="options.headerShow">
<text class="flip-item-header-text" :style="{
color: options.color
}">{{item.title}}</text>
</view>
<template v-if="item.type == 'text'">
<view class="flip-item-text flip-item-content"
>
<text class="flip-text"
v-for="(text, i) in item.text" :key="i"
:style="{
'margin-top': options.lineHeight + 'px',
'height': options.fontSize + 'px',
'font-size': options.fontSize + 'px',
'color': options.color
}">{{text}}</text>
</view>
</template>
<template v-else-if="item.type == 'custom'">
<view class="flip-custom flip-item-content" v-html="item.text">
</view>
</template>
<template v-else-if="item.type == 'slot'">
<view class="flip-slot flip-item-content">
<slot :name="item.text"></slot>
</view>
</template>
<template v-else-if="item.type == 'nextLoading' || item.type == 'prevLoading' ">
<view class="flip-loading flip-item-content">
<text :style="{
'color': options.color,
'font-size': options.fontSize + 'px'
}">正在加载内容</text>
</view>
</template>
<template v-else-if="item.type == 'top' || item.type == 'bottom' ">
<view class="flip-loading flip-item-content">
<text :style="{
'color': options.color,
'font-size': options.fontSize + 'px'
}">{{item.type == 'top' ? '前面已经没有了' : '后面已经没有了'}}</text>
</view>
</template>
<template v-else>
<view class="flip-loading flip-item-content">
<text :style="{
'color': options.color,
'font-size': options.fontSize + 'px'
}">未知类型页面</text>
</view>
</template>
<view class="flip-item-footer" v-if="options.footerShow">
<text class="flip-item-footer-text" :style="{
color: options.color
}">{{filterDate()}}</text>
<text class="flip-item-footer-text" :style="{
color: options.color
}">{{filterPage(item)}}</text>
<battery :color="options.color" style="opacity: 0.5"></battery>
</view>
</view>
<view
class="flip-item-bg"
:class="'flip-item-bg_' + item.dataId"
:style="{
left: viewWidth + 'px',
width: viewWidth + 'px',
height: (viewHeight * 1.5) + 'px',
top: (viewHeight / 2 - (viewHeight * 1.5) / 2) + 'px',
background: options.bgColor,
}"></view>
<view class="flip-item-shadow" :class="'flip-item-shadow_' + item.dataId"></view>
</view>
</template>
</view>
</template>
<script>
import Util from '../../../js_sdk/util.js'
import Battery from '../battery.vue'
export default {
components: {
Battery
},
props: {
currentDataId: {
type: Number,
default: -1
},
data: {
type: Array,
default () {
return new Array
}
},
options: {
type: Object,
default () {
return new Object
}
}
},
computed: {
dataReverse () {
let data = JSON.parse(JSON.stringify(this.data))
data.reverse()
return data
},
prevDataId () {
return this.dataReverse[this.current + 1] && this.dataReverse[this.current + 1].dataId
},
nextDataId () {
return this.dataReverse[this.current - 1] && this.dataReverse[this.current - 1].dataId
},
flipProp () {
return {
current: this.current,
prevDataId: this.prevDataId,
nextDataId: this.nextDataId,
currentDataId: this.currentDataIdSync,
pageType: this.options.pageType,
pageTo: this.pageTo
}
}
},
data () {
return {
isShow: false,
viewWidth: 0,
viewHeight: 0,
currentDataIdSync: -1,
current: -1,
pageTo: 0
}
},
mounted () {
this.currentDataIdSync = this.currentDataId
this.current = this.dataReverse.findIndex(item => item.dataId == this.currentDataIdSync)
this.$nextTick(function () {
setTimeout(() => {
// #ifndef APP-NVUE
const query = uni.createSelectorQuery().in(this);
query.select('.yingbing-flip').boundingClientRect(data => {
this.viewWidth = data.width
this.viewHeight = data.height
this.isShow = true
}).exec();
// #endif
// #ifdef APP-NVUE
uni.requireNativePlugin('dom').getComponentRect(this.$refs.yingbingFlip, res => {
this.viewWidth = res.size.width
this.viewHeight = res.size.height
this.isShow = true
})
// #endif
}, 50)
})
},
methods: {
onChange (e) {
this.current = e.current
this.currentDataIdSync = this.dataReverse[this.current].dataId
this.$emit('change', this.currentDataIdSync)
},
pagePrev () {
this.pageTo = 0
this.$nextTick(function(){
this.pageTo = -1
})
},
pageNext () {
this.pageTo = 0
this.$nextTick(function(){
this.pageTo = 1
})
},
filterPage (pageInfo) {
const nowChapters = this.data.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
let currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId)
if ( currentPage > -1 ) {
return (currentPage + 1) + ' / ' + nowChapters.length
} else {
return pageInfo.type == 'top' ? '最前面' : pageInfo.type == 'bottom' ? '最后面' : pageInfo.type.indexOf('Loading') > -1 ? '请等待' : ''
}
},
filterDate () {
let date = new Date()
return Util.zeroize(date.getHours()) + ':' + Util.zeroize(date.getMinutes())
}
},
watch: {
currentDataId (newVal) {
this.currentDataIdSync = newVal
this.current = this.dataReverse.findIndex(item => item.dataId == this.currentDataIdSync)
},
data (newVal) {
this.$nextTick(function () {
this.current = this.dataReverse.findIndex(item => item.dataId == this.currentDataIdSync)
})
}
}
}
</script>
<!-- #ifdef APP-VUE || H5 || MP-QQ || MP-WEIXIN -->
<script lang="wxs" module="flip">
function touchstart (event, ins) {
var state = ins.getState()
if ( state.isTouch || state.disableTouch ) {
return
}
state.isTouch = true
state.touchTime = 0
state.interval = true
setInterval(ins)
var touch = event.touches[0]
state.startX = touch.pageX
state.startY = touch.pageY
}
function touchmove (event, ins) {
event.preventDefault && event.preventDefault()
var state = ins.getState()
if ( state.isTouch && (state.pageType == 'real' || state.pageType == 'cover') && !state.disableTouch ) {
var touch = event.touches[0]
if (state.direction) {
var rect = ins.getBoundingClientRect()
var height = rect.height / 2;
var maxDeg = height / 5;
state.rotate = state.direction == 'next' ? ((touch.pageY - height) / maxDeg) : -((touch.pageY - height) / maxDeg);
state.offset = touch.pageX - state.startX;
if ( (state.offset > 0 && state.direction == 'next') || (state.offset < 0 && state.direction == 'prev') ) {
state.offset = 0
}
if ( Math.abs(state.offset) <= rect.width ) {
animation(state.offset, 0, ins);
}
} else {
if ( touch.pageX < state.startX ) {
if ( state.nextDataId ) {
state.direction = 'next'
}
} else {
if ( state.prevDataId ) {
state.direction = 'prev'
}
}
}
}
}
function touchend (event, ins) {
var state = ins.getState()
clearInterval(ins)
if ( state.isTouch && !state.disableTouch ) {
var rect = ins.getBoundingClientRect()
if ( !state.direction && state.touchTime <= 200 ) {
//
if (state.startX > (rect.width / 4) * 3) {
if ( state.nextDataId ) {
state.direction = 'next'
}
}
if (state.startX < (rect.width / 4)) {
if ( state.prevDataId ) {
state.direction = 'prev'
}
}
}
if (state.direction) {
state.disableTouch = true
if (state.touchTime <= 200) {
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 200 : 0
var value = state.direction == 'next' ? 1 : -1;
animation(-value * rect.width, duration, ins);
ins.setTimeout(function () {
reset(-value * rect.width, ins);
state.current -= value
ins.callMethod('onChange', {
current: state.current
})
}, duration)
} else {
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 100 : 0
if (Math.abs(state.offset) >= rect.width / 4) {
var value = state.direction == 'next' ? 1 : -1;
animation(-value * rect.width, duration, ins);
ins.setTimeout(function () {
reset(-value * rect.width, ins);
state.current -= value
ins.callMethod('onChange', {
current: state.current
})
}, duration)
} else {
animation(0, duration, ins);
ins.setTimeout(function () {
reset(0, ins);
}, duration)
}
}
} else {
reset(0, ins)
}
}
}
function propWatcher (newVal, oldVal, ins) {
ins.setTimeout(function () {
var state = ins.getState()
state.current = newVal.current
state.currentDataId = newVal.currentDataId
state.prevDataId = newVal.prevDataId
state.nextDataId = newVal.nextDataId
state.pageType = newVal.pageType
if (newVal.pageTo != (oldVal && oldVal.pageTo)) {
if ( !state.disableTouch ) {
if ( newVal.pageTo == -1 && state.prevDataId ) {
state.isTouch = true
state.startX = 1
state.touchTime = 0
state.direction = 'prev'
touchend(null, ins)
}
if ( newVal.pageTo == 1 && state.nextDataId ) {
state.isTouch = true
var rect = ins.getBoundingClientRect()
state.startX = rect.width
state.touchTime = 0
state.direction = 'next'
touchend(null, ins)
}
}
}
}, 50)
}
function setInterval (ins) {
var state = ins.getState()
state.touchTimer = ins.setTimeout(function () {
state.touchTime += 10
if ( state.interval ) {
setInterval(ins)
}
}, 10)
}
function clearInterval (ins) {
var state = ins.getState()
state.interval = false
if ( state.touchTimer ) {
ins.clearTimeout(state.touchTimer)
state.touchTimer = null
}
}
function reset (offset, ins) {
var state = ins.getState()
var rect = ins.getBoundingClientRect()
if ( state.direction ) {
var late = state.direction == 'next' ? offset : offset - rect.width;
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
if ( currentDataId ) {
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
transform: 'translateX(' + (-late) + 'px)',
'box-shadow': '',
transition: ''
})
if ( state.pageType == 'real' ) {
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
'box-shadow': '',
transition: ''
})
}
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
'box-shadow': '',
transition: ''
})
}
}
state.direction = null
state.isTouch = false
state.disableTouch = false
state.offset = 0
state.touchTime = 0
state.startX = 0
state.startY = 0
}
function animation (offset, duration, ins) {
var state = ins.getState()
var rect = ins.getBoundingClientRect()
var late = state.direction == 'next' ? offset : offset - rect.width;
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px)',
'box-shadow': state.pageType == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.pageType == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
if ( state.pageType == 'real' ) {
ins.selectComponent('.flip-item-wrapper_' + currentDataId).setStyle({
transform: 'translateX(' + (-late) + 'px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
'box-shadow': '-5px 0 20px rgba(0,0,0,0.1)',
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
})
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
'box-shadow': '0 0 60px ' + (Math.abs(late) > 30 ? 30 : Math.abs(late)) + 'px rgba(0,0,0,0.4)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
} else {
ins.selectComponent('.flip-item-wrapper_' + currentDataId).setStyle({
transform: 'translateX(0px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
'box-shadow': '0 0 60px 0 rgba(0,0,0,0.5)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
}
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
propWatcher: propWatcher
}
</script>
<!-- #endif -->
<style scoped>
.yingbing-flip {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: hidden;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
position: relative;
}
.flip-item {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.flip-item-wrapper {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.flip-item-header {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
box-sizing: border-box;
/* #endif */
justify-content: center;
height: 50rpx;
overflow: hidden;
}
.flip-item-header-text {
font-size: 24rpx;
opacity: .4;
font-weight: bold;
}
.flip-item-footer {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
align-items: center;
flex-direction: row;
justify-content: space-between;
height: 50rpx;
overflow: hidden;
}
.flip-item-footer-text {
font-size: 24rpx;
opacity: .4;
font-weight: bold;
}
.flip-item-content {
flex: 1;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.flip-item-text {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
},
.flip-text {
/* #ifndef APP-NVUE */
box-sizing: border-box;
white-space: pre-wrap;
font-family: Microsoft YaHei, 微软雅黑;
/* #endif */
}
.flip-loading {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.flip-item-bg {
position: absolute;
}
.flip-item-shadow {
position: absolute;
width: 0;
top: 0;
bottom: 0;
right: 0;
}
</style>

View File

@ -1,208 +0,0 @@
function touchstart (event, ins) {
var state = ins.getState()
if ( state.isTouch || state.disableTouch ) {
return
}
state.isTouch = true
state.touchTime = 0
state.interval = true
setInterval(ins)
var touch = event.touches[0]
state.startX = touch.pageX
state.startY = touch.pageY
}
function touchmove (event, ins) {
event.preventDefault && event.preventDefault()
var state = ins.getState()
if ( state.isTouch && (state.pageType == 'real' || state.pageType == 'cover') && !state.disableTouch ) {
var touch = event.touches[0]
if (state.direction) {
var rect = ins.getBoundingClientRect()
var height = rect.height / 2;
var maxDeg = height / 5;
state.rotate = state.direction == 'next' ? ((touch.pageY - height) / maxDeg) : -((touch.pageY - height) / maxDeg);
state.offset = touch.pageX - state.startX;
if ( (state.offset > 0 && state.direction == 'next') || (state.offset < 0 && state.direction == 'prev') ) {
state.offset = 0
}
if ( Math.abs(state.offset) <= rect.width ) {
animation(state.offset, 0, ins)
}
} else {
if ( touch.pageX < state.startX ) {
if ( state.nextDataId ) {
state.direction = 'next'
}
} else {
if ( state.prevDataId ) {
state.direction = 'prev'
}
}
}
}
}
function touchend (event, ins) {
var state = ins.getState()
clearInterval(ins)
if ( state.isTouch && !state.disableTouch ) {
var rect = ins.getBoundingClientRect()
if ( !state.direction && state.touchTime <= 200 ) {
//获取点击位置,判断向哪里翻页
if (state.startX > (rect.width / 4) * 3) {
if ( state.nextDataId ) {
state.direction = 'next'
}
}
if (state.startX < (rect.width / 4)) {
if ( state.prevDataId ) {
state.direction = 'prev'
}
}
}
if (state.direction) {
state.disableTouch = true
if (state.touchTime <= 200) {
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 200 : 0
var value = state.direction == 'next' ? 1 : -1;
animation(-value * rect.width, duration, ins);
ins.setTimeout(function () {
reset(-value * rect.width, ins);
ins.callMethod('onChange', value > 0 ? state.nextDataId : state.prevDataId)
}, duration)
} else {
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 100 : 0
if (Math.abs(state.offset) >= rect.width / 4) {
var value = state.direction == 'next' ? 1 : -1;
animation(-value * rect.width, duration, ins);
ins.setTimeout(function () {
reset(-value * rect.width, ins);
ins.callMethod('onChange', value > 0 ? state.nextDataId : state.prevDataId)
}, duration)
} else {
animation(0, duration, ins);
ins.setTimeout(function () {
reset(0, ins);
}, duration)
}
}
} else {
reset(0, ins)
}
}
}
function propWatcher (newVal, oldVal, ins) {
if ( oldVal ) {
var state = ins.getState()
state.currentDataId = newVal.currentDataId
state.prevDataId = newVal.prevDataId
state.nextDataId = newVal.nextDataId
state.pageType = newVal.pageType
if (newVal.pageTo != oldVal.pageTo) {
if ( !state.disableTouch ) {
if ( newVal.pageTo == -1 && state.prevDataId ) {
state.isTouch = true
state.startX = 1
state.touchTime = 0
state.direction = 'prev'
touchend(null, ins)
}
if ( newVal.pageTo == 1 && state.nextDataId ) {
state.isTouch = true
var rect = ins.getBoundingClientRect()
state.startX = rect.width
state.touchTime = 0
state.direction = 'next'
touchend(null, ins)
}
}
}
}
}
function setInterval (ins) {
var state = ins.getState()
state.touchTimer = ins.setTimeout(function () {
state.touchTime += 10
if ( state.interval ) {
setInterval(ins)
}
}, 10)
}
function clearInterval (ins) {
var state = ins.getState()
state.interval = false
if ( state.touchTimer ) {
ins.clearTimeout(state.touchTimer)
state.touchTimer = null
}
}
function reset (offset, ins) {
var state = ins.getState()
var rect = ins.getBoundingClientRect()
if ( state.direction ) {
var late = state.direction == 'next' ? offset : offset - rect.width;
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
if ( currentDataId ) {
var draw = function () {
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px)',
'box-shadow': '',
transition: ''
})
if ( state.pageType == 'real' ) {
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
'box-shadow': '',
transition: ''
})
}
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
'box-shadow': '',
transition: ''
})
}
ins.requestAnimationFrame(draw)
}
}
state.direction = null
state.isTouch = false
state.disableTouch = false
state.offset = 0
state.touchTime = 0
state.startX = 0
state.startY = 0
}
function animation (offset, duration, ins) {
var state = ins.getState()
var rect = ins.getBoundingClientRect()
var late = state.direction == 'next' ? offset : offset - rect.width;
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
var draw = function () {
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px)',
'box-shadow': state.pageType == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.pageType == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
if ( state.pageType == 'real' ) {
ins.selectComponent('.flip-item-wrapper_' + currentDataId).setStyle({
transform: 'translateX(' + (-late) + 'px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
'box-shadow': '-5px 0 20px rgba(0,0,0,0.1)',
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
})
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
'box-shadow': '0 0 60px 30px rgba(0,0,0,0.4)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
}
}
ins.requestAnimationFrame(draw)
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
propWatcher: propWatcher
}

View File

@ -76,7 +76,7 @@
})
},
setFontFace () {
this.$refs.webview.evalJS("setFontFace(" + encodeURIComponent(JSON.stringify(this.fontFace)) + ")")
this.$refs.webview && this.$refs.webview.evalJS("setFontFace(" + encodeURIComponent(JSON.stringify(this.fontFace)) + ")")
}
},
watch: {

View File

@ -23,7 +23,7 @@ export default {
return
}
this.scrolling = true
this.$refs.list.scrollTo(this.scrollTop + this.options.fontSize + this.options.lineHeight, true)
this.$refs.list.scrollTo(this.scrollTop + (this.windowHeight / 2), true)
this.scrollTimer = setTimeout(() => {
this.scrolling = false
clearTimeout(this.scrollTimer)
@ -35,14 +35,14 @@ export default {
return
}
this.scrolling = true
this.$refs.list.scrollTo(this.scrollTop - (this.options.fontSize + this.options.lineHeight), true)
this.$refs.list.scrollTo(this.scrollTop - (this.windowHeight / 2), true)
this.scrollTimer = setTimeout(() => {
this.scrolling = false
clearTimeout(this.scrollTimer)
this.scrollTimer = null
}, 300)
},
onPulldown (callback) {
pulldownScroll (callback) {
let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[0].chapter)
if ( this.contents[contentsIndex].isStart ) {
callback('end')
@ -64,7 +64,8 @@ export default {
this.$refs.list.resetLoadmore()
}
},
onLoadmore (callback) {
loadmoreScroll (callback) {
this.stopAutoplay()
let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[this.pages.length - 1].chapter)
if ( this.contents[contentsIndex].isEnd ) {
callback('end')
@ -103,12 +104,13 @@ export default {
});
this.preload(chapter)
}
callback && callback('success')
callback && callback(status)
})
}
},
async scrollEnd(e) {
let rate = Math.floor(e.scrollTop / this.viewHeight)
const size = await this.getRect()
let rate = Math.floor(e.scrollTop / size.height)
let maybe = this.pages[rate] ? rate : this.pages.length-1
let top = -1
let pageInfo = null
@ -127,7 +129,8 @@ export default {
//刷新当前时间和设备电量
this.scrollDate = this.filterDate()
this.$refs.scrollBattery.getBattery()
this.$emit('change', pageInfo, this.pages)
this._emitPageInfo(pageInfo, this.pages)
this.startAutoplay()
}
},
getScrollItemRect (dataId) {
@ -138,6 +141,7 @@ export default {
})
},
onScroll (e) {
this.stopAutoplay()
if ( this.options.pageType == 'scroll' ) {
this.scrollTop = e.scrollTop
if ( this.scrollTimer ) {

View File

@ -1,67 +1,46 @@
<template>
<view class="yingbing-read-page" ref="yingbingReadPage" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
<!-- <computed ref="computedPage" :options="options"></computed> -->
<view class="computed">
<text class="computed-text computed-text-chinese" ref="computedTextChinese" :style="{
'font-family': fontFamily
}"></text>
<text class="computed-text computed-text-space" ref="computedTextSpace" :style="{
'font-family': fontFamily
}">s</text>
<text class="computed-text computed-text-lower" ref="computedTextLower" :style="{
'font-family': fontFamily
}">a</text>
<text class="computed-text computed-text-upper" ref="computedTextUpper" :style="{
'font-family': fontFamily
}">A</text>
<text class="computed-text computed-text-number" ref="computedTextNumber" :style="{
'font-family': fontFamily
}">9</text>
<text class="computed-text computed-text-special" ref="computedTextSpecial" :style="{
'font-family': fontFamily
}">&</text>
</view>
<!-- 翻页模式 -->
<view class="yingbing-read-page-flip" :style="{
'visibility': pageType != 'scroll' ? 'visible' : 'hidden'
}">
<template v-if="pageType != 'scroll'">
<!-- #ifndef APP-NVUE -->
<view
class="yingbing-flip"
ref="yingbingFlip"
:style="{
'background': options.bgColor
}"
:prop="flipProp"
:change:prop="flip.propWatcher"
@touchstart="flip.touchstart"
@touchmove="flip.touchmove"
@touchend="flip.touchend">
<yingbing-flip ref="flip" class="yingbing-read-page-flip" :data="pages" :current="current" :bgColor="bgColor" :duration="300" :unableClickPage="unableClickPage" :type="pageType" @change="handleFlipChangeRender">
<!-- #ifdef MP -->
<template v-for="(item, index) in pages" :slot="index">
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view
class="yingbing-flip"
ref="yingbingFlip"
:style="{
'background': options.bgColor
}"
@touchstart="onFilpTouchstart"
@touchmove="onFilpTouchmove"
@touchend="onFilpTouchend">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<template v-if="isShow">
<view
class="flip-item"
v-for="(item, index) in dataReverse"
:key="item.dataId + '_flip'"
:style="{
'visibility': item.dataId == currentDataId || item.dataId == prevDataId || item.dataId == nextDataId ? 'visible' : 'hidden'
<view class="flip-item-wrapper" :style="{
'padding-left': slide + 'px',
'padding-right': slide + 'px',
'padding-top': topGap + 'px',
'padding-bottom': bottomGap + 'px'
}">
<view
v-if="item.dataId == currentDataId || item.dataId == prevDataId || item.dataId == nextDataId"
class="flip-item"
:ref="'flipItem_' + item.dataId"
:class="'flip-item_' + item.dataId"
:style="{
'transform': item.dataId < currentDataId ? `translateX(${-viewWidth}px)` : '',
}">
<view
class="flip-item-wrapper"
:ref="'flipItemWrapper_' + item.dataId"
:class="'flip-item-wrapper_' + item.dataId"
:style="{
'padding-left': options.slide + 'px',
'padding-right': options.slide + 'px',
'padding-top': options.topGap + 'px',
'padding-bottom': options.bottomGap + 'px',
'background': options.bgColor,
'transform': item.dataId < currentDataId ? options.pageType == 'real' ? `translateX(${viewWidth}px)` : 'translateX(0)' : ''
}">
<view class="flip-item-header" v-if="options.headerShow">
<view class="flip-item-header" v-if="headerShow">
<text class="flip-item-header-text" :style="{
color: options.color,
'font-family': options.fontFamily
color: color,
'font-family': fontFamily
}">{{item.title}}</text>
</view>
<template v-if="item.type == 'text'">
@ -74,79 +53,72 @@
'height': options.fontSize + 'px',
'font-size': options.fontSize + 'px',
'color': options.color,
'font-family': options.fontFamily
'font-family': options.fontFamily,
'line-height': options.fontSize + 'px'
}">{{text}}</text>
</view>
</template>
<template v-else-if="item.type == 'custom'">
<template v-if="item.type == 'custom'">
<read-rich-text style="flex: 1;" :richtext="item.text" :fontFace="fontFace" :pageType="pageType" @customClick="customClick"></read-rich-text>
</template>
<template v-else-if="item.type == 'slot'">
<template v-if="item.type == 'slot'">
<view class="flip-slot flip-item-content">
<!-- #ifndef MP-WEIXIN -->
<slot :name="item.text" :prop="item"></slot>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<slot :name="item.text"></slot>
<!-- #endif -->
</view>
</template>
<template v-else-if="item.type == 'nextLoading' || item.type == 'prevLoading' ">
<template v-if="item.type == 'loading'">
<view class="flip-loading flip-item-content">
<text :style="{
<text class="flip-loading-text" :style="{
'color': options.color,
'font-size': options.fontSize + 'px',
'font-family': options.fontFamily
}">正在加载内容</text>
</view>
</template>
<template v-else-if="item.type == 'top' || item.type == 'bottom' ">
<template v-if="item.type == 'fail' || item.type == 'timeout' ">
<view class="flip-loading flip-item-content">
<text :style="{
<text class="flip-loading-text" @tap="reloadLoadmoreFlip(item)" :style="{
'color': options.color,
'font-size': options.fontSize + 'px',
'font-family': options.fontFamily
}">{{item.type == 'top' ? '前面已经没有了' : '后面已经没有了'}}</text>
}">{{item.type == 'timeout' ? '加载超时' : '加载失败'}},点击刷新</text>
</view>
</template>
<template v-else>
<template v-if="item.type == 'top' || item.type == 'bottom' ">
<view class="flip-loading flip-item-content">
<text :style="{
<slot :name="item.type">
<text class="flip-loading-text" :style="{
'color': options.color,
'font-family': options.fontFamily
}">{{item.type == 'top' ? firstTip : lastTip}}</text>
</slot>
</view>
</template>
<!-- <template v-else>
<view class="flip-loading flip-item-content">
<text class="flip-loading-text" :style="{
'color': options.color,
'font-size': options.fontSize + 'px',
'font-family': options.fontFamily
}">未知类型页面</text>
</view>
</template>
<view class="flip-item-footer" v-if="options.footerShow">
<!-- <text class="flip-item-footer-text" :style="{
color: options.color,
'font-family': options.fontFamily
}">{{filterDate()}}</text> -->
</template> -->
<view class="flip-item-footer" v-if="footerShow">
<text class="flip-item-footer-text" :style="{
color: options.color,
'font-family': options.fontFamily
color: color,
'font-family': fontFamily
}">{{filterDate()}}</text>
<text class="flip-item-footer-text" :style="{
color: color,
'font-family': fontFamily
}">{{filterPage(item)}}</text>
<!-- <battery :color="options.color" style="opacity: 0.5"></battery> -->
</view>
</view>
<view
class="flip-item-bg"
:ref="'flipItemBg_' + item.dataId"
:class="'flip-item-bg_' + item.dataId"
:style="{
left: viewWidth + 'px',
width: viewWidth + 'px',
height: (viewHeight * 1.5) + 'px',
transform: item.dataId < currentDataId && options.pageType == 'real' ? 'translateX(' + viewWidth + 'px)' : '',
top: (viewHeight / 2 - (viewHeight * 1.5) / 2) + 'px',
background: options.bgColor,
}"></view>
<view
class="flip-item-shadow"
:ref="'flipItemShadow_' + item.dataId"
:class="'flip-item-shadow_' + item.dataId">
</view>
<battery :color="color" style="opacity: 0.5"></battery>
</view>
</view>
</template>
</view>
</template>
</yingbing-flip>
</view>
<!-- 翻页模式 -->
@ -171,16 +143,16 @@
<scroll-list
ref="list"
@scroll="onScroll"
:pulldown="{show: true, color: options.color, endText: '已经到最前面了'}"
:loadmore="{show: true, color: options.color, endText: '已经到最后面了'}"
@pulldown="onPulldown"
@loadmore="onLoadmore"
:pulldown="{show: true, color: options.color, defaultText: '下拉获取上一章节', endText: firstTip, failText: '获取上一章节失败'}"
:loadmore="{show: true, color: options.color, defaultText: '上拉获取上一章节', endText: lastTip, failText: '获取下一章节失败'}"
@pulldown="pulldownScroll"
@loadmore="loadmoreScroll"
@scrolltoupper="scrolltoupper">
<view class="scroll-item-wrapper" ref="scrollItemWrapper">
<view
:id="'scroll-item_' + item.dataId"
v-for="(item, index) in pages"
:key="item.dataId + '_scroll'"
:key="item.dataId"
class="scroll-item"
:ref="'scrollItem_' + item.dataId"
:style="{
@ -197,7 +169,8 @@
'height': options.fontSize + 'px',
'font-size': options.fontSize + 'px',
'color': options.color,
'font-family': options.fontFamily
'font-family': options.fontFamily,
'line-height': options.fontSize + 'px'
}">{{text}}</text>
</view>
</template>
@ -248,8 +221,13 @@
</view> -->
</template>
</view>
<view class="yingbing-loading" v-if="initLoading" :style="{background: options.bgColor}" @tap="reload">
<list-loading :size="40":visible="initLoading" :color="options.color" :text="loadingText"></list-loading>
<view class="yingbing-loading" v-if="loadstatus" :style="{background: options.bgColor}" @tap="_reload">
<text class="error-text" :style="{
color: options.color
}">{{loadstatus == 'fail' ? '加载失败' : '加载超时'}},点击刷新</text>
</view>
<view class="yingbing-loading" v-if="initLoading" :style="{background: options.bgColor}">
<list-loading :size="40" :visible="initLoading" :color="options.color" text="正在加载中"></list-loading>
</view>
</view>
</template>
@ -275,6 +253,16 @@
ListLoading
},
props: {
///
autoplay: {
type: Boolean,
default: false
},
///
autoplayTime: {
type: [String, Number],
default: 5000
},
//
color: {
type: String,
@ -297,11 +285,34 @@
return new Array
}
},
firstTip: {
type: String,
default: '前面已经没有了'
},
lastTip: {
type: String,
default: '后面已经没有了'
},
//
firstTipUnable: {
type: Boolean,
default: false
},
//
lastTipUnable: {
type: Boolean,
default: false
},
//
bgColor: {
type: String,
default: '#fcd281'
},
//2
unableClickPage: {
type: Boolean,
default: false
},
//
pageType: {
type: String,
@ -352,6 +363,11 @@
type: Boolean,
default: true
},
//
split: {
type: String,
default: ''
},
//
clickOption: {
type: Object,
@ -363,7 +379,7 @@
top: 'auto'
}
}
},
}
},
data () {
return {
@ -379,7 +395,11 @@
touchmoveY: 0,
touchTime: 0,
windowWidth: 0,
windowHeight: 0
windowHeight: 0,
initLoading: true,
loadstatus: '',
loadchapter: -1,
loadstart: -1
}
},
computed: {
@ -388,6 +408,7 @@
},
options () {
return {
unableClickPage: this.unableClickPage,
pageType: this.pageType,
color: this.color,
bgColor: this.bgColor,
@ -405,10 +426,8 @@
}
},
beforeDestroy () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.clearRefreshTimer()
this.stopAutoplay()
},
mounted () {
this.$nextTick(function () {
@ -423,6 +442,7 @@
},
methods: {
touchstart (e) {
this.stopAutoplay()
if ( !this.enableClick ) {
return
}
@ -449,6 +469,7 @@
this.touchmoveY = touch.pageY;
},
touchend (e) {
this.startAutoplay()
if ( this.touchInter ) {
clearTimeout(this.touchInter);
this.touchInter = null
@ -501,11 +522,9 @@
this.$emit('setCatalog', e);
},
//
init (data) {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
async init (data) {
this.clearRefreshTimer()
await this.getComputedTextSize()
if ( !this.noChapter ) {
this.contents = data.contents;
this.initLoading = true;
@ -519,21 +538,57 @@
},
//
refresh () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
this.stopAutoplay()
this.clearRefreshTimer()
if ( this.isRefreshing ) {
this.refreshTimer = setTimeout(() => {
this.refresh()
}, 100)
return
}
this.isRefreshing = true
this.resetPage({
start: this.pageInfo.start,
currentChapter: this.pageInfo.chapter
})
},
_emitPageInfo (pageInfo, pages) {
this.isRefreshing = false
this.$emit('change', pageInfo, pages)
},
_reload () {
this.initLoading = true
if ( this.loadchapter > -1 ) {
this.$emit('loadmore', parseInt(this.loadchapter), (status, content) => {
this.initLoading = false
if (status == 'success') {
this._resetReload()
const index = this.contents.findIndex(item => item.chapter == content.chapter)
if (index > -1) {
this.contents[index] = content
} else {
this.contents.push(content)
}
this.resetPage({
start: this.loadstart || 0,
currentChapter: this.loadchapter
})
} else {
this.loadstatus = status
}
})
}
},
_resetReload () {
this.loadstatus = '';
this.loadchapter = -1;
this.loadstart = -1;
},
//
change (data) {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.stopAutoplay()
this.clearRefreshTimer()
this._resetReload()
if ( data.contents && data.contents.length > 0 ) {
data.contents.forEach(item => {
let index = this.contents.findIndex(content => content.chapter == item.chapter)
@ -552,10 +607,71 @@
currentChapter: parseInt(data.currentChapter || 1)
})
} else {
if ( this.noChapter ) {
uni.showToast({
title: '未找到该章节内容',
title: '未找到该章节',
icon: 'none'
})
} else {
this.initLoading = true;
this.$emit('loadmore', parseInt(data.currentChapter), (status, content) => {
this.initLoading = false
if (status == 'success') {
const index = this.contents.findIndex(item => item.chapter == content.chapter)
if (index > -1) {
this.contents[index] = content
} else {
this.contents.push(content)
}
this.resetPage({
start: parseInt(data.start || 0),
currentChapter: parseInt(data.currentChapter || 1)
})
} else {
this.loadstatus = status;
this.loadstart = parseInt(data.start || 0)
this.loadchapter = parseInt(data.currentChapter || 1);
}
})
}
}
},
//
pagePrev () {
this.stopAutoplay()
if ( this.options.pageType != 'scroll' ) {
this.pagePrevFlip()
} else {
this.scrollPrev()
}
},
//
pageNext () {
this.stopAutoplay()
if ( this.options.pageType != 'scroll' ) {
this.pageNextFlip()
} else {
this.scrollNext()
}
},
startAutoplay () {
if ( this.autoplay && this.pages.length > 0 && this.pages.findIndex(page => page.dataId == this.currentDataId) < this.pages.length - 1 ) {
this.stopAutoplay()
this.autoplayTimer = setTimeout(() => {
this.pageNext()
}, this.autoplayTime)
}
},
stopAutoplay () {
if ( this.autoplayTimer ) {
clearTimeout(this.autoplayTimer)
this.autoplayTimer = null
}
},
clearRefreshTimer () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
},
initFont () {
@ -576,84 +692,58 @@
})
// #endif
})
},
}
},
watch: {
pageType (newVal, oldVal) {
this.$nextTick(function () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.refreshTimer = setTimeout(() => {
if ( newVal != 'scroll' ) {
this.getViewRect().then(res => {
this.refresh()
})
} else {
this.refresh()
}
}, 100)
})
},
fontSize () {
this.$nextTick(function () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.refreshTimer = setTimeout(() => {
this.refresh()
}, 100)
})
},
lineHeight () {
this.$nextTick(function () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.refreshTimer = setTimeout(() => {
this.refresh()
}, 100)
})
},
slide () {
this.$nextTick(function () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.refreshTimer = setTimeout(() => {
this.refresh()
}, 100)
})
},
topGap () {
this.$nextTick(function () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.refreshTimer = setTimeout(() => {
this.refresh()
}, 100)
})
},
bottomGap () {
this.$nextTick(function () {
if ( this.refreshTimer ) {
clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
this.refreshTimer = setTimeout(() => {
this.refresh()
}, 100)
})
},
firstTipUnable () {
this.$nextTick(function () {
this.refresh()
})
},
lastTipUnable () {
this.$nextTick(function () {
this.refresh()
})
},
fontFamily () {
this.$nextTick(async function () {
await this.getComputedTextSize()
this.refresh()
})
},
fontFace () {
this.initFont()
},
}
}
}
</script>
@ -680,11 +770,9 @@
}
</script>
<!-- #endif -->
<!-- #ifdef APP-VUE || H5 || MP-QQ || MP-WEIXIN -->
<script lang="wxs" module="flip" src="../modules/flip/flip.wxs"></script>
<!-- #endif -->
<style scoped>
@import url(../modules/computed/computed.css);
@import url(../modules/flip/flip.css);
@import url(../modules/scroll/scroll.css);
.yingbing-read-page {
@ -712,6 +800,9 @@
align-items: center;
justify-content: center;
}
.yingbing-loading .error-text {
font-size: 20px;
}
.yingbing-slot {
/* #ifndef APP-NVUE */
display: flex;

View File

@ -1,8 +1,8 @@
{
"id": "yingbing-ReadPage",
"displayName": "小说阅读分页插件",
"version": "1.4.3",
"description": "给小说分页的插件,包含翻页",
"displayName": "好用阅读分页插件",
"version": "1.5.9",
"description": "给小说分页的插件",
"keywords": [
"小说",
"阅读",

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
## 1.0.42023-09-02
* 解决插槽中有scroll-view等滚动组件时滑动报错的问题
## 1.0.32023-08-23
* 解决APP-NVUE端resetLoading不生效的问题
## 1.0.22023-07-25
* 解决竖直翻页时点击上下2侧翻页不准确得问题
## 1.0.12023-07-24
* 解决APP-NVUE无法点击左右2侧翻页得问题
## 1.0.02023-07-22
* 第一次更新

View File

@ -0,0 +1,367 @@
function touchstart (event, ins) {
var state = ins.getState()
if ( state.isTouch || state.disableTouch ) {
return
}
state.isTouch = true
state.touchTime = 0
state.interval = true
setInterval(ins)
var touch = event.touches[0]
state.startX = touch.pageX
state.startY = touch.pageY
}
function touchmove (event, ins) {
var state = ins.getState()
if ( state.isTouch && !state.disableTouch ) {
var touch = event.touches[0]
state.offset = state.vertical ? touch.pageY - state.startY : touch.pageX - state.startX;
if (state.direction) {
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width
state.offset = state.direction == 'next' ? state.offset + state.sliderFault : state.offset - state.sliderFault
if ( (state.offset > 0 && state.direction == 'next') || (state.offset < 0 && state.direction == 'prev') ) {
state.offset = 0
}
if ( Math.abs(state.offset) <= size ) {
animation(state.offset, 0, ins)
}
} else {
if ( Math.abs(state.offset) < state.sliderFault ) {
return
}
if ( state.offset < 0 ) {
if ( state.nextIndex < state.count && state.nextIndex != 0 ) {
if ( state.type != 'none' ) {state.direction = 'next'}
} else if ( state.pullupable && state.loadingState != 'loading' && state.loadingState != 'success' && state.loadingState != 'fail' ) {
state.loadingType = 'pullup'
state.offset = state.offset + state.sliderFault
pulling(state.offset, ins)
}
} else {
if ( state.prevIndex >= 0 && state.prevIndex != state.count - 1 ) {
if ( state.type != 'none' ) {state.direction = 'prev'}
} else if ( state.pulldownable && state.loadingState != 'loading' && state.loadingState != 'success' && state.loadingState != 'fail' ) {
state.loadingType = 'pulldown'
state.offset = state.offset - state.sliderFault
pulling(state.offset, ins)
}
}
}
}
}
function touchend (event, ins) {
touchaction(event, ins)
}
function touchcancel (event, ins) {
touchaction(event, ins)
}
function touchaction (event, ins,isFlipTo) {
var state = ins.getState()
clearInterval(ins)
if ( state.isTouch && !state.disableTouch ) {
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width
var start = state.vertical ? state.startY : state.startX
if ( !state.direction && state.touchTime <= 200 && (!state.unableClickPage || state.type == 'none') && !isFlipTo ) {
//获取点击位置,判断向哪里翻页
if (start > (size / 4) * 3 && state.nextIndex < state.count && state.nextIndex != 0 ) {
state.direction = 'next'
}
if (start < (size / 4) && state.prevIndex >= 0 && state.prevIndex != state.count - 1 ) {
state.direction = 'prev'
}
}
if (state.direction) {
state.disableTouch = true
if (state.touchTime <= 200) {
var duration = state.type == 'none' ? 0 : state.duration;
var value = state.direction == 'next' ? 1 : -1;
animation(-value * size, duration, ins);
ins.setTimeout(function () {
resetShadow(ins, true)
ins.callMethod('handleFlipChange', value);
}, duration + 50)
} else {
var duration = state.type == 'none' ? 0 : state.duration;
if (Math.abs(state.offset) >= size / 4) {
var value = state.direction == 'next' ? 1 : -1;
animation(-value * size, duration, ins);
ins.setTimeout(function () {
resetShadow(ins, true)
ins.callMethod('handleFlipChange', value);
}, duration + 50)
} else {
animation(0, duration, ins);
ins.setTimeout(function () {
resetShadow(ins)
resetFlip(ins);
}, duration + 50)
}
}
} else if ( state.loadingState == 'default' ) {
resetPulling(ins)
} else if ( state.loadingState == 'ready' ) {
pullingRefresh(ins)
} else {
resetShadow(ins, false)
resetFlip(ins)
}
}
}
function propWatcher (newVal, oldVal, ins) {
ins.setTimeout(function () {
var state = ins.getState()
state.vertical = newVal.vertical
state.pulldownable = newVal.pulldownable
state.pullupable = newVal.pullupable
state.pulldownHeight = newVal.pulldownHeight
state.loadingState = newVal.loadingState
state.pullupHeight = newVal.pullupHeight
state.duration = newVal.duration
state.nextIndex = newVal.nextIndex
state.prevIndex = newVal.prevIndex
state.currentIndex = newVal.currentIndex
state.sliderFault = newVal.sliderFault
state.count = newVal.count
state.type = newVal.type
state.unableClickPage = newVal.unableClickPage
state.translate = newVal.translate
if ( oldVal && newVal.currentIndex != oldVal.currentIndex ) {
resetFlip(ins);
}
if ( oldVal && newVal.loadingState != oldVal.loadingState && state.loadingState ) {
resetPulling(ins)
ins.callMethod('resetLoading')
}
if (oldVal && newVal.flipTo != oldVal.flipTo && newVal.flipTo != 0 ) {
if ( !state.disableTouch ) {
if ( newVal.flipTo < 0 && state.prevIndex >= 0 && (state.prevIndex != state.count - 1) ) {
state.isTouch = true
state.touchTime = 0
state.direction = 'prev'
touchaction(null, ins, true)
}
if ( newVal.flipTo > 0 && state.nextIndex < state.count && state.nextIndex != 0 ) {
state.isTouch = true
state.touchTime = 0
state.direction = 'next'
touchaction(null, ins, true)
}
}
}
}, 100)
}
function setInterval (ins) {
var state = ins.getState()
state.touchTimer = ins.setTimeout(function () {
state.touchTime += 10
if ( state.interval ) {
setInterval(ins)
}
}, 10)
}
function clearInterval (ins) {
var state = ins.getState()
state.interval = false
if ( state.touchTimer ) {
ins.clearTimeout(state.touchTimer)
state.touchTimer = null
}
}
function resetShadow (ins, isChange) {
var state = ins.getState()
var direction = state.direction
if ( !direction ) {
return
}
var index = direction == 'next' ? state.currentIndex : state.prevIndex
var translate = state.translate
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width;
var draw = function () {
ins.selectComponent('.yingbing-flip-item_' + index).setStyle({
'box-shadow': '',
transform: translate + '(' + (isChange ? (direction == 'next'? -size : 0) : (direction == 'next'? 0 : -size)) + 'px)',
transition: ''
})
if ( state.type == 'real' ) {
ins.selectComponent('.yingbing-flip-item-bg_' + index).setStyle({
'box-shadow': '',
transform: translate + '(' + (isChange ? (direction == 'next' ? 0 : size) : (direction == 'next' ? size : 0)) + 'px)',
transition: ''
})
}
ins.selectComponent('.yingbing-flip-item-shadow_' + index).setStyle({
'box-shadow': '',
transition: ''
})
}
ins.requestAnimationFrame(draw)
}
function resetFlip (ins) {
var state = ins.getState()
state.direction = null
state.isTouch = false
state.disableTouch = false
state.offset = 0
state.touchTime = 0
state.startX = 0
state.startY = 0
}
function animation (offset, duration, ins, noshadow) {
var state = ins.getState()
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width
var translate = state.translate
var late = offset
var draw = function () {
if ( state.direction == 'prev' ) {
if ( state.prevIndex >= 0 ) {
ins.selectComponent('.yingbing-flip-item_' + state.prevIndex).setStyle({
transform: translate + '(' + (late - size) + 'px)',
'box-shadow': noshadow ? '' : state.type == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.type == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
if ( state.type == 'real' ) {
ins.selectComponent('.yingbing-flip-item-content_' + state.prevIndex).setStyle({
transform: translate + '(' + (size-late) + 'px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-bg_' + state.prevIndex).setStyle({
transform: translate + '(' + (late) + 'px)',
'box-shadow': noshadow ? '' : '-5px 0 20px rgba(0,0,0,0.1)',
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-shadow_' + state.prevIndex).setStyle({
'box-shadow': noshadow ? '' : '0 0 60px 30px rgba(0,0,0,0.4)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
}
}
} else {
if ( state.nextIndex < state.count ) {
ins.selectComponent('.yingbing-flip-item_' + state.nextIndex).setStyle({
transform: translate + '(0)',
transition: ''
})
}
ins.selectComponent('.yingbing-flip-item_' + state.currentIndex).setStyle({
transform: translate + '(' + late + 'px)',
'box-shadow': noshadow ? '' : state.type == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.type == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
if ( state.type == 'real' ) {
ins.selectComponent('.yingbing-flip-item-content_' + state.currentIndex).setStyle({
transform: translate + '(' + (-late) + 'px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-bg_' + state.currentIndex).setStyle({
transform: translate + '(' + (late + size) + 'px)',
'box-shadow': noshadow ? '' : '-5px 0 20px rgba(0,0,0,0.1)',
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-shadow_' + state.currentIndex).setStyle({
'box-shadow': noshadow ? '' : '0 0 60px 30px rgba(0,0,0,0.4)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
}
}
}
ins.requestAnimationFrame(draw)
}
function pulling (offset, ins) {
var state = ins.getState()
var loadingType = state.loadingType
var translate = state.translate
var size = loadingType == 'pullup' ? state.pullupHeight : state.pulldownHeight
var late = offset
if ( Math.abs(state.offset) < size ) {
state.loadingState = 'default'
} else {
state.loadingState = 'ready'
}
var draw = function () {
var pullingItems = ins.selectAllComponents('.yingbing-flip-' + loadingType + '-item')
for ( var i = 0; i < pullingItems.length; i++ ) {
if ( pullingItems[i].hasClass('yingbing-flip-' + loadingType + '-' + state.loadingState) ) {
pullingItems[i].setStyle({
visibility: 'visible'
})
} else {
pullingItems[i].setStyle({
visibility: 'hidden'
})
}
}
if ( Math.abs(late) <= size ) {
ins.selectComponent('.yingbing-flip-' + loadingType).setStyle({
transform: translate + '(' + (loadingType == 'pullup' ? late + size : late - size) + 'px)',
transition: ''
})
}
}
ins.requestAnimationFrame(draw)
}
function resetPulling (ins) {
var state = ins.getState()
var loadingType = state.loadingType
var translate = state.translate
var size = loadingType == 'pullup' ? state.pullupHeight : state.pulldownHeight
var draw = function () {
var pullingItems = ins.selectAllComponents('.yingbing-flip-' + loadingType + '-item')
for ( var i = 0; i < pullingItems.length; i++ ) {
if ( pullingItems[i].hasClass('yingbing-flip-' + loadingType + '-' + state.loadingState) ) {
pullingItems[i].setStyle({
visibility: 'visible'
})
} else {
pullingItems[i].setStyle({
visibility: 'hidden'
})
}
}
ins.selectComponent('.yingbing-flip-' + loadingType).setStyle({
transform: translate + '(' + (loadingType == 'pullup' ? size : -size) + 'px)',
transition: 'transform .3s'
})
}
ins.requestAnimationFrame(draw)
if ( state.loadingState ) {
ins.setTimeout( function () {
state.loadingState = ''
resetPulling(ins)
}, 300)
} else {
state.loadingType = ''
resetFlip(ins)
}
}
function pullingRefresh (ins) {
var state = ins.getState()
state.loadingState = 'loading'
var loadingType = state.loadingType
var draw = function () {
var pullingItems = ins.selectAllComponents('.yingbing-flip-' + loadingType + '-item')
for ( var i = 0; i < pullingItems.length; i++ ) {
if ( pullingItems[i].hasClass('yingbing-flip-' + loadingType + '-loading') ) {
pullingItems[i].setStyle({
visibility: 'visible'
})
} else {
pullingItems[i].setStyle({
visibility: 'hidden'
})
}
}
}
ins.requestAnimationFrame(draw)
ins.callMethod('pullingRefresh', state.loadingType)
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
touchcancel: touchcancel,
propWatcher: propWatcher
}

View File

@ -0,0 +1,546 @@
const Binding = uni.requireNativePlugin('bindingx')
const animation = uni.requireNativePlugin('animation')
const dom = uni.requireNativePlugin('dom')
import Util from '../../js_sdk/util.js'
export default {
data () {
return {
disableTouch: false,
isTouch: false,
touchTime: 0,
interval: false,
loadingType: '',
direction: ''
}
},
beforeDestroy () {
if ( this.flipBinding ) {
Binding.unbind({
token: this.flipBinding.token,
eventType: 'pan'
})
this.sliderBinding = null
}
this.resetFlipAnimationBinding()
},
methods: {
fliptouchstart (event) {
if ( this.isTouch || this.disableTouch ) {
return
}
this.isTouch = true
this.touchTime = 0
this.interval = true
this.setInterval()
let touch = event.touches[0]
this.startX = touch.pageX
this.startY = touch.pageY
},
fliptouchmove (event) {
if ( this.isTouch && !this.disableTouch ) {
let touch = event.touches[0]
let offset = this.vertical ? touch.pageY - this.startY : touch.pageX - this.startX
if ( !this.direction ) {
if ( Math.abs(offset) < this.sliderFault ) return
if ( offset < 0 ) {
if ( this.nextIndex < this.count && this.nextIndex != 0 ) {
if ( this.type != 'none' ) {this.direction = 'next'}
} else if ( this.pullupable && this.loadingState != 'loading' && this.loadingState != 'success' && this.loadingState != 'fail' ) {
this.loadingType = 'pullup'
this.disableTouch = true
this.clearInterval()
this.pulling()
}
} else {
if ( this.prevIndex > -1 && this.prevIndex != this.count - 1 ) {
if ( this.type != 'none' ) {this.direction = 'prev'}
} else if ( this.pulldownable && this.loadingState != 'loading' && this.loadingState != 'success' && this.loadingState != 'fail' ) {
this.loadingType = 'pulldown'
this.disableTouch = true
this.clearInterval()
this.pulling()
}
}
}
if ( this.direction ) {
this.disableTouch = true
this.flipTouchAction()
} else {
this.resetPageBinding()
}
}
},
async fliptouchend (e) {
if ( this.isTouch && !this.disableTouch ) {
let rect = await this.getRect(this.$refs.yingbingFlip)
let size = this.vertical ? rect.height : rect.width
this.clearInterval()
let start = this.vertical ? this.startY : this.startX
if ( this.touchTime <= 200 && (!this.unableClickPage || this.type == 'none') ) {
if (start > (size / 4) * 3 && this.nextIndex < this.count && this.nextIndex != 0 ) {
this.flipToNextBindingX()
} else if (start < (size / 4) && this.prevIndex >= 0 && this.prevIndex != this.count - 1 ) {
this.flipToPrevBindingX()
} else {
this.resetPageBinding()
}
} else {
this.resetPageBinding()
}
}
},
async flipTouchAction () {
let props = []
let rect = await this.getRect(this.$refs.yingbingFlip)
let size = this.vertical ? rect.height : rect.width
let translate = this.translate
let shadowProperty = this.vertical ? 'height' : 'width'
let key = this.vertical ? 'y' : 'x'
if ( this.direction == 'prev' ) {
if ( this.prevIndex > -1 ) {
props.push({
element: this.getEl('yingbingFlipItem_' + this.prevIndex),
property: 'transform.' + translate,
expression: `${key} < 0 ? ${-size} : (${key} > ${size} ? 0 : ${key}-${size})`
})
if ( this.type == 'real' ) {
props.push({
element: this.getEl('yingbingFlipItemContent_' + this.prevIndex),
property: 'transform.' + translate,
expression: `${key} < 0 ? ${size} : (${key} > ${size} ? 0 : ${size}-${key})`
})
props.push({
element: this.getEl('yingbingFlipItemBg_' + this.prevIndex),
property: 'transform.' + translate,
expression: `${key} < 0 ? 0 : (${key} > ${size} ? ${size} : ${key} + 0)`
})
props.push({
element: this.getEl('yingbingFlipItemShadow_' + this.prevIndex),
property: shadowProperty,
expression: `${size} / 2 - abs(${key}) / 2'`
})
}
}
} else {
if ( this.nextIndex < this.count ) {
props.push({
element: this.getEl('yingbingFlipItem_' + this.nextIndex),
property: 'transform.' + translate,
expression: '0+0'
})
}
props.push({
element: this.getEl('yingbingFlipItem_' + this.currentIndex),
property: 'transform.' + translate,
expression: `${key} > 0 ? 0 : (${key} < ${-size} ? ${-size} : ${key} + 0)`
})
if ( this.type == 'real' ) {
props.push({
element: this.getEl('yingbingFlipItemContent_' + this.currentIndex),
property: 'transform.' + translate,
expression: `${key} > 0 ? 0 : (${key} < ${-size} ? ${size} : 0 - ${key})'`
})
props.push({
element: this.getEl('yingbingFlipItemBg_' + this.currentIndex),
property: 'transform.' + translate,
expression: `${key} > 0 ? ${size} : (${key} < ${-size} ? 0 : ${key}+${size})`
})
props.push({
element: this.getEl('yingbingFlipItemShadow_' + this.currentIndex),
property: shadowProperty,
expression: `abs(${key})/2+0`
})
}
}
this.flipBinding = Binding.bind({
anchor: this.getEl('yingbingFlip'),
eventType: 'pan',
props: props
}, (e) => {
if ((e.state == 'end' || e.state == 'cancel') && this.flipBinding) {
this.clearInterval()
Binding.unbind({
token: this.flipBinding.token,
eventType: 'pan'
})
this.flipBinding = null
let value = this.direction == 'next' ? 1 : -1;
if (this.touchTime <= 200) {
this.pageAnimation(-value * size, size);
} else {
let index = this.direction == 'next' ? this.currentIndex : this.prevIndex
let deltaX = Binding.getComputedStyle(this.getEl('yingbingFlipItem_' + index))[translate]
let offset = this.direction == 'next' ? Math.abs(deltaX) : size - Math.abs(deltaX)
if ( offset >= size / 4) {
this.pageAnimation(-value * size, size)
} else {
this.pageAnimation(0, size);
}
}
}
})
},
flipToNextBindingX () {
if ( !this.disableTouch && this.nextIndex < this.count && (this.nextIndex != 0 || this.circular) ) {
this.direction = 'next'
this.flipToBindingX()
}
},
flipToPrevBindingX () {
if ( !this.disableTouch && this.prevIndex >= 0 && (this.prevIndex != this.count - 1 || this.circular) ) {
this.direction = 'prev'
this.flipToBindingX()
}
},
async flipToBindingX () {
this.disableTouch = true
let value = this.direction == 'next' ? 1 : -1;
let rect = await this.getRect(this.$refs.yingbingFlip)
let size = this.vertical ? rect.height : rect.width
this.pageAnimation(-value * size, size)
},
getRect (el) {
return new Promise(resolve => {
dom.getComponentRect(el, res => {
resolve(res.size)
})
})
},
setInterval () {
this.touchTimer = setTimeout(() => {
this.touchTime += 10
if ( this.interval ) {
this.setInterval()
}
}, 10)
},
clearInterval () {
this.interval = false
if ( this.touchTimer ) {
clearTimeout(this.touchTimer)
this.touchTimer = null
}
},
pageAnimation (offset, size) {
let duration = this.type == 'none' ? 0.1 : this.duration
let late = offset
let translate = this.translate
let shadowProperty = this.vertical ? 'height' : 'width'
let props = []
if ( this.direction == 'prev' ) {
if ( this.prevIndex > -1 ) {
let itemTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItem_' + this.prevIndex))[translate]
props.push({
element: this.getEl('yingbingFlipItem_' + this.prevIndex),
property: 'transform.' + translate,
expression: `linear(t, ${itemTrans}, ${late - size - itemTrans}, ${duration})`
})
if ( this.type == 'real' ) {
let contentTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemContent_' + this.prevIndex))[translate]
props.push({
element: this.getEl('yingbingFlipItemContent_' + this.prevIndex),
property: 'transform.' + translate,
expression: `linear(t, ${contentTrans}, ${-(late - size) - contentTrans}, ${duration})`
})
let bgTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemBg_' + this.prevIndex))[translate]
props.push({
element: this.getEl('yingbingFlipItemBg_' + this.prevIndex),
property: 'transform.' + translate,
expression: `linear(t, ${bgTrans}, ${late - bgTrans}, ${duration})`
})
let shadowSize = Binding.getComputedStyle(this.getEl('yingbingFlipItemShadow_' + this.prevIndex))[shadowProperty] || size
props.push({
element: this.getEl('yingbingFlipItemShadow_' + this.prevIndex),
property: shadowProperty,
expression: `linear(t, ${shadowSize}, ${-(late - size) - shadowSize}, ${duration})`
})
}
this.flipAnimationBinding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flipAnimationBinding && e.t > duration) {
Binding.unbind({
token: this.flipAnimationBinding.token,
eventType: 'timing'
})
this.flipAnimationBinding = null
if ( Math.abs(offset) > 0 ) {
this.handleFlipChange(this.direction == 'next' ? 1 : -1)
this.$nextTick(function () {
this.resetPageBinding()
})
} else {
this.resetPageBinding();
}
}
})
}
} else {
if ( this.nextIndex < this.count ) {
animation.transition(this.getRef('yingbingFlipItem_' + this.nextIndex), {
styles: {
transform: `${translate}(0)`
},
duration: 0,
timingFunction: 'linear',
needLayout: true
})
}
animation.transition(this.getRef('yingbingFlipItem_' + this.currentIndex), {
styles: {
transform: `${translate}(${late}px)`
},
duration: duration,
timingFunction: 'linear',
needLayout: true
}, () => {
if ( Math.abs(offset) > 0 ) {
this.handleFlipChange(this.direction == 'next' ? 1 : -1)
this.$nextTick(function () {
this.resetPageBinding()
})
} else {
this.resetPageBinding();
}
})
if ( this.type == 'real' ) {
animation.transition(this.getRef('yingbingFlipItemContent_' + this.currentIndex), {
styles: {
transform: `${translate}(${-late}px)`
},
duration: duration,
timingFunction: 'linear',
needLayout: true
})
animation.transition(this.getRef('yingbingFlipItemBg_' + this.currentIndex), {
styles: {
transform: `${translate}(${size + late}px)`
},
duration: duration,
timingFunction: 'linear',
needLayout: true
})
let styles = {}
styles[shadowProperty] = -late + 'px'
animation.transition(this.getRef('yingbingFlipItemShadow_' + this.currentIndex), {
styles: styles,
duration: duration,
timingFunction: 'linear',
needLayout: true
})
}
// if ( this.nextIndex < this.count ) {
// props.push({
// element: this.getEl('yingbingFlipItem_' + this.nextIndex),
// property: 'transform.' + translate,
// expression: '0+0'
// })
// }
// let itemTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItem_' + this.currentIndex))[translate]
// props.push({
// element: this.getEl('yingbingFlipItem_' + this.currentIndex),
// property: 'transform.' + translate,
// expression: `linear(t, ${itemTrans}, ${late - itemTrans}, ${duration})`
// })
// if ( this.type == 'real' ) {
// let contentTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemContent_' + this.currentIndex))[translate]
// props.push({
// element: this.getEl('yingbingFlipItemContent_' + this.currentIndex),
// property: 'transform.' + translate,
// expression: `linear(t, ${contentTrans}, ${-late - contentTrans}, ${duration})`
// })
// let bgTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemBg_' + this.currentIndex))[translate]
// props.push({
// element: this.getEl('yingbingFlipItemBg_' + this.currentIndex),
// property: 'transform.' + translate,
// expression: `linear(t, ${bgTrans}, ${size + late - bgTrans}, ${duration})`
// })
// let shadowSize = Binding.getComputedStyle(this.getEl('yingbingFlipItemShadow_' + this.currentIndex))[shadowProperty]
// props.push({
// element: this.getEl('yingbingFlipItemShadow_' + this.currentIndex),
// property: shadowProperty,
// expression: `linear(t, ${shadowSize}, ${-late - shadowSize}, ${duration})`
// })
// }
}
},
pulling () {
let loadingType = this.loadingType
let size = loadingType == 'pullup' ? this.pullupHeight : this.pulldownHeight
let key = this.vertical ? 'y' : 'x'
let translate = this.translate
let props = [
{
element: this.getEl('yingbing_flip_' + loadingType),
property: 'transform.' + translate,
expression: loadingType == 'pullup' ? `abs(${key}) > ${size} ? 0 : ${key} + ${size}` : `abs(${key}) > ${size} ? 0 : ${key} - ${size}`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_default'),
property: 'opacity',
expression: `abs(${key}) < ${size} ? 1 : 0`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_ready'),
property: 'opacity',
expression: `abs(${key}) < ${size} ? 0 : 1`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_loading'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_success'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_fail'),
property: 'opacity',
expression: '0+0'
}
]
this.flipBinding = Binding.bind({
anchor: this.getEl('yingbingFlip'),
eventType: 'pan',
props: props
}, (e) => {
if ((e.state == 'end' || e.state == 'cancel') && this.flipBinding) {
Binding.unbind({
token: this.flipBinding.token,
eventType: 'pan'
})
this.flipBinding = null
let deltaX = Binding.getComputedStyle(this.getEl('yingbing_flip_' + loadingType))[translate]
if ( deltaX == 0 ) {
this.loadingState = 'ready'
this.pullingRefreshBindingx()
} else {
this.loadingState = 'default'
this.resetPullingBindingx()
}
}
})
},
resetPullingBindingx () {
let loadingType = this.loadingType
let translate = this.translate
let size = loadingType == 'pullup' ? this.pullupHeight : this.pulldownHeight
let trans = loadingType == 'pullup' ? size : -size
let deltaX = Binding.getComputedStyle(this.getEl('yingbing_flip_' + loadingType))[translate]
let duration = 300
let props = [
{
element: this.getEl('yingbing_flip_' + loadingType),
property: 'transform.' + translate,
expression: `linear(t, ${deltaX}, ${trans}, ${duration})`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_default'),
property: 'opacity',
expression: this.loadingState == 'default' ? '1+0' : '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_ready'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_loading'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_success'),
property: 'opacity',
expression: this.loadingState == 'success' ? '1+0' : '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_fail'),
property: 'opacity',
expression: this.loadingState == 'fail' ? '1+0' : '0+0'
}
]
this.flipAnimationBinding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flipAnimationBinding && e.t > duration) {
this.resetFlipAnimationBinding()
this.loadingState = ''
this.resetPageBinding()
}
})
},
pullingRefreshBindingx () {
let loadingType = this.loadingType
let duration = 1
let props = [
{
element: this.getEl('yingbing_flip_' + loadingType + '_default'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_ready'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_loading'),
property: 'opacity',
expression: '1+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_success'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_fail'),
property: 'opacity',
expression: '0+0'
}
]
this.flipAnimationBinding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flipAnimationBinding && e.t > duration) {
this.resetFlipAnimationBinding()
this.pullingRefresh(loadingType)
}
})
},
resetFlipAnimationBinding () {
if ( this.flipAnimationBinding ) {
Binding.unbind({
token: this.flipAnimationBinding.token,
eventType: 'timing'
})
this.flipAnimationBinding = null
}
},
getEl (selector) {
return this.$refs[selector] ? Util.getEl(this.$refs[selector].length > 0 ? this.$refs[selector][0] : this.$refs[selector]) : null
},
getRef (selector) {
return this.$refs[selector] ? this.$refs[selector].length > 0 ? this.$refs[selector][0] : this.$refs[selector] : null
},
resetPageBinding () {
this.direction = ''
this.touchTime = 0
this.startX = 0
this.startY = 0
this.$nextTick(function () {
this.isTouch = false
this.disableTouch = false
})
}
}
}

View File

@ -0,0 +1,420 @@
<template>
<!-- #ifndef APP-NVUE -->
<view
class="yingbing-flip"
:prop="flipProp"
:change:prop="flip.propWatcher"
@touchstart="flip.touchstart"
@touchmove="flip.touchmove"
@touchend="flip.touchend"
@touchcancel="flip.touchcancel"
:style="{
background: bgColor
}">
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view
class="yingbing-flip"
ref="yingbingFlip"
@touchstart="fliptouchstart"
@touchmove="fliptouchmove"
@touchend="fliptouchend"
:style="{
background: bgColor
}">
<!-- #endif -->
<view
class="yingbing-flip-item"
:class="'yingbing-flip-item_' + item"
v-for="(item, index) in dataSync"
:ref="'yingbingFlipItem_' + item"
:style="{
'background': bgColor,
'transform': item > currentIndex ? translate + '(' + fullSize + ')' : item < currentIndex ? translate + '(-' + fullSize + ')' : '',
'box-shadow': vertical ? '0 0 15rpx rgba(0,0,0,.2)' : ''
}"
:key="item">
<view
class="yingbing-flip-item-content"
:ref="'yingbingFlipItemContent_' + item"
:class="'yingbing-flip-item-content_' + item"
:style="{
'background': bgColor,
'transform': item < currentIndex ? type == 'real' ? translate + '(' + fullSize + ')' : translate + '(0)' : translate + '(0)'
}">
<!-- #ifdef MP -->
<slot v-if="item > -1 && item < count" :name="item"></slot>
<!-- #endif -->
<!-- #ifndef MP -->
<slot v-if="item > -1 && item < count" :item="data[item]" :index="item"></slot>
<!-- #endif -->
</view>
<view
class="yingbing-flip-item-bg"
:ref="'yingbingFlipItemBg_' + item"
:class="'yingbing-flip-item-bg_' + item"
:style="{
background: bgColor,
transform: item < currentIndex && type == 'real' ? translate + '(0)' : translate + '(' + fullSize + ')',
}"></view>
<view
class="yingbing-flip-item-shadow"
:ref="'yingbingFlipItemShadow_' + item"
:class="'yingbing-flip-item-shadow_' + item"
:style="[shadowStyle]"></view>
</view>
<view class="yingbing-flip-pulldown"
:style="[pulldownStyle]" v-if="pulldownable" ref="yingbing_flip_pulldown">
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-default" ref="yingbing_flip_pulldown_default">
<slot name="pulldownDefault"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-ready" ref="yingbing_flip_pulldown_ready">
<slot name="pulldownReady"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-loading" ref="yingbing_flip_pulldown_loading">
<slot name="pulldownLoading"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-success" ref="yingbing_flip_pulldown_success">
<slot name="pulldownSuccess"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-fail" ref="yingbing_flip_pulldown_fail">
<slot name="pulldownFail"></slot>
</view>
</view>
<view class="yingbing-flip-pullup"
:style="[pullupStyle]" v-if="pullupable" ref="yingbing_flip_pullup">
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-default" ref="yingbing_flip_pullup_default">
<slot name="pullupDefault"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-ready" ref="yingbing_flip_pullup_ready">
<slot name="pullupReady"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-loading" ref="yingbing_flip_pullup_loading">
<slot name="pullupLoading"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-success" ref="yingbing_flip_pullup_success">
<slot name="pullupSuccess"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-fail" ref="yingbing_flip_pullup_fail">
<slot name="pullupFail"></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
import flipBindingx from '../modules/flip_bindingx.js'
// #endif
export default {
// #ifdef APP-NVUE
mixins: [flipBindingx],
// #endif
props: {
data: {
type: Array,
default () {
return new Array
}
},
vertical: {
type: Boolean,
default: false
},
current: {
type: Number,
default: 0
},
//
type: {
type: String,
default: 'real'
},
//
duration: {
type: Number,
default: 100
},
//
sliderFault: {
type: Number,
default: 20
},
//
bgColor: {
type: String,
default: '#fcd281'
},
//2
unableClickPage: {
type: Boolean,
default: false
},
//
pulldownable: {
type: Boolean,
default: false
},
//
pulldownHeight: {
type: Number,
default: 80
},
//
pullupable: {
type: Boolean,
default: false
},
//
pullupHeight: {
type: Number,
default: 80
}
},
computed: {
dataSync () {
let arr = []
if ( this.prevIndex >= 0 ) {
arr.push(this.prevIndex)
}
arr.push(this.currentIndex)
if ( this.nextIndex < this.count ) {
arr.push(this.nextIndex)
}
return this.refreshing ? [] : arr.sort((a, b) => b-a)
},
nextIndex () {
return this.currentIndex + 1 > this.count - 1 && this.count > 2 ? 0 : this.currentIndex + 1
},
prevIndex () {
return this.currentIndex - 1 < 0 && this.count > 2 ? this.count - 1 : this.currentIndex - 1
},
count () {
return this.data.length
},
flipProp () {
return {
vertical: this.vertical,
pulldownable: this.pulldownable,
pullupable: this.pullupable,
pulldownHeight: this.pulldownHeight,
pullupHeight: this.pullupHeight,
loadingState: this.loadingState,
duration: this.duration,
unableClickPage: this.unableClickPage,
nextIndex: this.nextIndex,
prevIndex: this.prevIndex,
currentIndex: this.currentIndex,
type: this.type,
count: this.count,
flipTo: this.flipTo,
sliderFault: this.sliderFault,
translate: this.translate
}
},
pulldownStyle () {
return this.vertical ? {
left: 0,
right: 0,
top: 0,
height: this.pulldownHeight + 'px',
transform: this.translate + '(-' + this.pulldownHeight + 'px)'
} : {
left: 0,
top: 0,
bottom: 0,
width: this.pulldownHeight + 'px',
transform: this.translate + '(-' + this.pulldownHeight + 'px)'
}
},
pullupStyle () {
return this.vertical ? {
left: 0,
right: 0,
bottom: 0,
height: this.pullupHeight + 'px',
transform: this.translate + '(' + this.pullupHeight + 'px)'
} : {
right: 0,
top: 0,
bottom: 0,
width: this.pullupHeight + 'px',
transform: this.translate + '(' + this.pullupHeight + 'px)'
}
},
shadowStyle () {
return this.vertical ? {
bottom: 0,
right: 0,
left: 0,
height: 0,
// #ifdef APP-NVUE
'background-image': 'linear-gradient(to bottom, rgba(255,255,255, 0), rgba(0,0,0,.5))'
// #endif
} : {
top: 0,
bottom: 0,
right: 0,
width: 0,
// #ifdef APP-NVUE
'background-image': 'linear-gradient(to right, rgba(255,255,255, 0), rgba(0,0,0,.5))'
// #endif
}
},
translate () {
return this.vertical ? 'translateY' : 'translateX'
},
fullSize () {
return this.vertical ? '3050rpx' : '750rpx'
}
},
data () {
return {
refreshing: false,
currentIndex: 0,
flipTo: 0,
loadingState: '',
}
},
created() {
this.currentIndex = this.current
},
methods: {
handleFlipChange (value) {
if ( value > 0 ) {
this.currentIndex = this.currentIndex + value > this.count - 1 ? 0 : this.currentIndex + value
} else {
this.currentIndex = this.currentIndex + value < 0 ? this.count - 1 : this.currentIndex + value
}
this.$emit('change', {
current: this.currentIndex,
detail: this.data[this.currentIndex]
})
this.$emit('update:current', this.currentIndex)
},
pullingRefresh (type) {
this.$emit(type, (state) => {
this.loadingState = state
// #ifdef APP-NVUE
this.resetPullingBindingx()
// #endif
})
},
flipToNext () {
// #ifdef APP-NVUE
this.flipToNextBindingX()
// #endif
// #ifndef APP-NVUE
this.flipTo = 0
this.$nextTick(function () {
this.flipTo = 1
})
// #endif
},
flipToPrev () {
// #ifdef APP-NVUE
this.flipToPrevBindingX()
// #endif
// #ifndef APP-NVUE
this.flipTo = 0
this.$nextTick(function () {
this.flipTo = -1
})
// #endif
},
refresh () {
this.refreshing = true
this.$nextTick(function () {
this.currentIndex = this.current
this.refreshing = false
})
},
resetLoading () {
this.loadingState = ''
// #ifdef APP-NVUE
this.resetPullingBindingx()
// #endif
}
},
watch: {
current (newVal) {
this.currentIndex = newVal
}
}
}
</script>
<!-- #ifdef APP-VUE || H5 || MP-QQ || MP-WEIXIN -->
<script lang="wxs" module="flip" src="../modules/flip.wxs"></script>
<!-- #endif -->
<style>
.yingbing-flip {
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
overflow: hidden;
position: relative;
}
.yingbing-flip-item {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
overflow: hidden;
/* #endif */
}
.yingbing-flip-item-content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.yingbing-flip-item-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* #ifdef APP-NVUE */
box-shadow: 0 0 20rpx rgba(0,0,0,0.2);
/* #endif */
}
.yingbing-flip-item-shadow {
position: absolute;
/* #ifdef APP-NVUE */
opacity: 0.5;
/* #endif */
}
.yingbing-flip-pulldown, .yingbing-flip-pullup {
position: absolute;
}
.yingbing-flip-pulldown-item, .yingbing-flip-pullup-item {
/* #ifndef APP-NVUE */
visibility: hidden;
/* #endif */
/* #ifdef APP-NVUE */
opacity: 0;
/* #endif */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* #ifdef MP */
/deep/ .scoped-ref {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
yingbing-flip {
display: block;
}
/* #endif */
</style>

View File

@ -0,0 +1,446 @@
export default {
/**
* 补零
* @param {Number} val 数字
**/
zeroize (val) {
return zeroize(val);
},
/**
* 时间格式化
* @param {String} time 时间戳or时间
**/
dateFormat (time, formats = 'yyyy-mm-dd hh:mm:ss') {
let arr = formats.split(' ')
let dateFormats = ''
let timeFormats = ''
arr.forEach(item => {
if ( item.indexOf('yy') > -1 ) {
dateFormats = item
} else {
timeFormats = item
}
})
const d = new Date(time);
let result = ''
if ( dateFormats.indexOf('yyyy') > -1 ) {
result += d.getFullYear() + '-'
}
if ( dateFormats.indexOf('mm') > -1 ) {
result += zeroize(d.getMonth() + 1) + '-'
}
if ( dateFormats.indexOf('dd') > -1 ) {
result += zeroize(d.getDate()) + ' '
}
if ( timeFormats.indexOf('hh') > -1 ) {
result += zeroize(d.getHours()) + ':'
}
if ( timeFormats.indexOf('mm') > -1 ) {
result += zeroize(d.getMinutes()) + ':'
}
if ( timeFormats.indexOf('ss') > -1 ) {
result += zeroize(d.getSeconds()) + ':'
}
return result.substring(0, result.length - 1)
},
/**
* 秒数转化为分秒
* @param {String} value 秒数
**/
minutesFormat (value) {
let minutes = Math.floor(value / 60 % 60) >= 10 ? Math.floor(value / 60 % 60) : '0' + Math.floor(value / 60 % 60);
let seconds = Math.floor(value % 60) >= 10 ? Math.floor(value % 60) : '0' + Math.floor(value % 60);
return minutes + ':' + seconds;
},
/**
* 时间转化为秒数
* @param {String} time 时间HH:mm:ss
**/
time2seconds (time){
const seconds = parseInt(time.split(':')[0]) * 60 + parseInt(time.split(':')[1].split('.')[0]) + parseInt(time.split(':')[1].split('.')[1]) / 1000;
return seconds;
},
/**
* 移除url地址域名
* @param {String} str http地址
**/
removeUrl (url) {
let str = url.replace(/^http:\/\/[^/]+/, '');
return str.substr(1);
},
/**
* 获取文件后缀
* @param {String} name 带后缀的文件名称
**/
suffix (name) {
//获取图片后缀
let fileName = name.lastIndexOf(".");
let fileNameLength = name.length;
let fileFormat = name.substring(fileName + 1, fileNameLength);
return fileFormat;
},
/**
* 清除文件后缀
* @param {String} name 带后缀的文件名称
*/
removeSuffix (name) {
//获取图片后缀
let fileName = name.lastIndexOf(".");
if ( fileName > -1 ) {
let fileNameFormat = name.substring(0, fileName);
return fileNameFormat;
} else {
return name
}
},
/**
* 数组查找符合条件元素并返回下标
* @param {Array} arr 传入数组
* @param {String} value 条件元素
* @param {String} query 对比key值
*/
indexOf (arr, query, value) {
let len = arr.length;
for ( let i = 0; i < len; i++ ) {
if ( arr[i][query] == value ) {
return parseInt(i);
}
}
return -1;
},
/**
* 正则匹配
* @param {String} type 匹配类型
* @param {String} value 匹配值
*/
reg (type, value) {
const regs = {
//身份证证则
idcard: new RegExp(/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/),
//手机正则
mobile: new RegExp(/^1[3456789]\d{9}$/),
//固定电话正则
phone: new RegExp(/^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/),
//金额验证
price: new RegExp(/^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0.\d{1,2}$/),
//邮箱验证
email: new RegExp(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/),
//银行卡
bankcard: new RegExp(/^([1-9]{1})(\d{15}|\d{18})$/)
}
return regs[type].test(value);
},
/**
* 计算2个时间差的分钟数或者秒钟数或时钟数
* @param {datetime} time1 开始时间
* @param {datetime} time2 结束时间
*/
timeMinuse (time1, time2, type = 'minutes') {
//判断开始时间是否大于结束日期
let date1 = new Date(time1);
let date2 = new Date(time2);
if ( date1 > date2 ) {
console.log("开始时间不能大于结束时间!");
return false;
}
let seconds = date2.getTime() / 1000 - date1.getTime() / 1000;
return type == 'minutes' ? (seconds / 60) : type == 'hours' ? (seconds / 60 / 60) : seconds;
},
/**
* 判断值类型返回字符
* @param {datetime} value 需要判断类型的值
*/
typeof (value) {
let type = Object.prototype.toString.call(value);
return type.slice(8, type.length - 1)
},
/**
* 生成随机字符串
* @param {Number} len 长度
*/
randomString (len) {
len = len || 32;
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
var maxPos = $chars.length;
var pwd = '';
for (let i = 0; i < len; i++) {
  pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
},
/**
* 生成随机ID
*/
randomID () {
let mydate = new Date();
return mydate.getMinutes() + mydate.getSeconds() + mydate.getMilliseconds() + Math.round(Math.random() * 10000);
},
/**
* 生成随机不重复整数
* @param {Number} len 长度
*/
randomSoleNumber (len) {
let min = 0;
let max = len - 1;
let arr = [];
while ( arr.length < len ) {
let value = Math.floor(Math.random() * (max - min + 1)) + min;
if ( arr.indexOf(value) == -1 ) {
arr.push( value )
}
}
return arr;
},
/**
* 16进制颜色转化为rgb
* @param {String} hex 16进制颜色
*/
hex2rgb (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
},
/**
* 16进制颜色转化为rgba
* @param {String} hex 16进制颜色
*/
hex2rgba (hex, opacity) {
hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
let str="rgba("
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+","+opacity+")";
return str
},
/**
* byte转化为文件大小
* @param {Number} byte
*/
byte2Size (byte) {
let sizeString = ''
if(byte == 0){
sizeString = "0B";
}else if(byte < 1024){
sizeString = byte + "B";
}else if(byte < 1048576){
sizeString = (byte/1024).toFixed(2) + "KB";
}else if (byte < 1073741824){
sizeString = (byte/1048576).toFixed(2) + "MB";
}else{
sizeString = (byte/1073741824).toFixed(2) + "GB";
}
return sizeString;
},
// 深度克隆
deepClone (obj) {
if(typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = isArray(obj) ? [] : {};
for(let i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? this.deepClone(obj[i]) : obj[i];
}
}
return o;
},
/**
* 将数字转为带中文单位的字符串
* @param {Number} num 数字
*/
numtounit (num) {
let units = [{
label: '万',
value: 10000,
min: 1000
},{
label: '亿',
value: 100000000,
min: 100000000
},{
label: '兆',
value: 10000000000000000,
min: 100000000000000000
}]
let value = num
units.forEach(unit => {
if ( num >= unit.min ) {
value = (num / unit.value).toFixed(2) + unit.label
}
})
return value
},
/**
* 判断像素单位没有则加上rpx
* @param {String} value 像素
*/
pixelunit (value) {
if ( value.toString().indexOf('px') > -1 || value.toString().indexOf('em') > -1 || value.toString().indexOf('auto') > -1 || value.toString().indexOf('%') > -1 ) {
return value
} else {
return value + 'rpx'
}
},
/**
* 判断像素单位全部转为px
* @param {String} value 像素
*/
unitpixel (value) {
if ( value.toString().indexOf('rpx') > -1 ) {
return uni.upx2px(value.replace('rpx', ''))
} else if ( value.toString().indexOf('px') > -1 ) {
return parseFloat(value.replace('px', ''))
} else if ( value.toString().indexOf('em') > -1 || value.toString().indexOf('auto') > -1 || value.toString().indexOf('%') > -1 ) {
return value
} else {
return parseFloat(uni.upx2px(value))
}
},
/**
* 判断像素单位转化为rpx
* @param {String} value
* @param {String} unit 返回结果是否带上单位
*/
anytorpx (value, unit = true) {
if ( value.toString().indexOf('rpx') > -1 ) {
return unit ? value : parseFloat(value.replace('rpx', ''))
} else if ( value.toString().indexOf('px') > -1 ) {
return parseFloat(value.replace('px', '') * (750 / uni.getSystemInfoSync().windowWidth)) + (unit ? 'rpx' : 0)
} else if ( value.toString().indexOf('auto') > -1 ) {
return 'auto'
} else if ( value.toString().indexOf('%') > -1 ) {
return parseFloat((value.replace('%', '') / 100) * 750) + (unit ? 'rpx' : 0)
} else if (value.toString().indexOf('em') > -1 || value.toString().indexOf('rem') > -1 ) {
return parseFloat(value.replace('em', '').replace('rem', '') * 32) + (unit ? 'rpx' : 0)
} else if ( /^\d+$/.test(value) ) {
return parseFloat(value) + (unit ? 'rpx' : 0)
}
},
/**
* 判断像素单位转化为px
* @param {String} value
* @param {String} unit 返回结果是否带上单位
*/
anytopx (value, unit = false) {
if ( value.toString().indexOf('rpx') > -1 ) {
return uni.upx2px(value.replace('rpx', '')) + (unit ? 'px' : 0)
} else if ( value.toString().indexOf('px') > -1 ) {
return parseFloat(value.replace('px', '')) + (unit ? 'px' : 0)
} else if ( value.toString().indexOf('auto') > -1 ) {
return 'auto'
} else if ( value.toString().indexOf('%') > -1 ) {
return parseFloat((value.replace('%', '') / 100) * uni.getSystemInfoSync().windowWidth) + (unit ? 'px' : 0)
} else if (value.toString().indexOf('em') > -1 || value.toString().indexOf('rem') > -1 ) {
return parseFloat(value.replace('em', '').replace('rem', '') * uni.getSystemInfoSync().windowWidth) + (unit ? 'px' : 0)
} else if ( /^\d+$/.test(value) ) {
return parseFloat(value) + (unit ? 'px' : 0)
}
},
getRefs (components, name, current) {
// #ifndef MP
return current >= 0 ? components.$refs[name][current] : components.$refs[name]
// #endif
// #ifdef MP
return {}
// #endif
},
//获取节点
getEl (el) {
if (typeof el === 'string' || typeof el === 'number') return el;
if (WXEnvironment) {
return el.ref;
} else {
return el instanceof HTMLElement ? el : el.$el;
}
},
/**
* 获取指定父节点
* @param {String} components 当前实例
* @param {String} name 父节点名称
*/
getParent(name, components) {
let parent = components.$parent
if (parent) {
let parentName = parent.$options.name
while (parentName !== name) {
parent = parent.$parent
if (parent) {
parentName = parent.$options.name
} else {
return null
}
}
return parent
}
return null
},
/**
* 获取指定子节点
* @param {String} components 当前实例
* @param {String} name 父节点名称
*/
getChildrens(names, components) {
let arr = []
let childs = names.split(',')
const dowhile = (children) => {
if ( this.typeof(children) == 'Array' ) {
children.forEach(child => {
if ( childs.indexOf(child.$options.name) > -1 ) {
arr.push(child)
}
if ( child.$children && child.$children.length > 0 ) {
dowhile(child.$children)
}
})
}
}
dowhile(components.$children)
return arr;
},
/**
* 获取指定子节点
* @param {String} selector 节点class或者id
* @param {String} el 节点
* @param {String} components 当前实例
*/
getRect (selector, el, components) {
return new Promise(resolve => {
// #ifdef APP-NVUE
uni.requireNativePlugin('dom').getComponentRect(el, res => {
resolve(res.size)
})
// #endif
// #ifndef APP-NVUE
uni.createSelectorQuery().in(components).select(selector).boundingClientRect(data => {
resolve(data)
}).exec();
// #endif
})
}
}
// 判断arr是否为一个数组返回一个bool值
function isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function zeroize (val) {
return val >= 10 ? val : '0' + val;
}

View File

@ -0,0 +1,81 @@
{
"id": "yingbing-flip",
"displayName": "好用翻页组件",
"version": "1.0.4",
"description": "高性能翻页组件",
"keywords": [
"翻页"
],
"repository": "https://gitee.com/yingbing-developer/yingbing-flip.git",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,344 @@
#使用须知
* 1、这是一个翻页组件适用于小说翻页或答题功能
* 2、这个插件支持APP-NVUE、APP-VUE、H5、微信小程序
* 3、该组件滑动项同时只能存在当前项、前面项、后面项3个其余项会被销毁
* 4、如果想要构建阅读器需要配合[好用阅读分页插件](https://ext.dcloud.net.cn/plugin?id=6026)使用
* 5、有什么不懂可以加群 1087735942 聊
#props属性
| 属性名 | 类型 | 默认值 | 可选值 | 说明 |
| :----- | :----: | :----: | :----: | :---- |
| data | Array | ---- | ---- | 列表数据 |
| current | Number | 0 | ---- | 初始化位置 |
| vertical | Boolean | false | true/false | 垂直滑动 |
| duration | Number | 100 | ---- | 滑动动画时间 |
| bgColor | String | ---- | ---- | 背景色 |
| pulldownable | Boolean | false | true/false | 开启下拉刷新 |
| pullupable | Boolean | false | true/false | 开启上拉加载 |
| pulldownHeight | Number | 80 | ---- | 下拉刷新控件高度px |
| pullupHeight | Number | 80 | ---- | 上拉加载控件高度px |
| sliderFault | Number | 20 | ---- | 滑动容错距离px |
| type | String | real | real仿真/cover覆盖/none无动画 | 翻页方式 |
| unableClickPage | Boolean | false | true/false | 关闭点击左右2侧翻页功能type为none时忽略此属性 |
#event事件
| 事件名 | 参数 | 说明 |
| :----- | :----: | :---- |
| change | current: 当前滑动位置, detail: 当前滑动位置列表数据 | 位置改变事件 |
| pulldown | callback: 回调 | 下拉刷新事件 |
| pullup | callback: 回调 | 上拉加载事件 |
#内置方法
| 方法名 | 参数 | 说明 |
| :--- | :------ | :---- |
| refresh | ---- | 刷新滑动位置(替换数据时使用) |
| flipToNext | ---- | 翻到到下个位置 |
| flipToPrev | ---- | 翻到上个位置 |
#slot插槽
| 名称 | 说明 |
| :----- | :---- | :---- |
| pulldownDefault | 下拉加载默认提示 |
| pulldownReady | 下拉加载准备提示 |
| pulldownLoading | 下拉加载等待提示 |
| pulldownSuccess | 下拉加载成功提示 |
| pulldownFail | 下拉加载失败提示 |
| pullupDefault | 上拉加载默认提示 |
| pullupReady | 上拉加载准备提示 |
| pullupLoading | 上拉加载等待提示 |
| pullupSuccess | 上拉加载成功提示 |
| pullupFail | 上拉加载失败提示 |
#快速开始
```html
<yingbing-flip :data="list" style="height: 100vh;">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<!-- #ifdef MP -->
<template v-for="(item, index) in list" :slot="index">
<!-- #endif -->
<view style="height: 100%">
<text style="font-size: 50px;font-weight: bold;color: #fff;">{{item}}</text>
</view>
</template>
</yingbing-flip>
```
```javascript
export default {
data () {
return {
list: []
}
},
onReady () {
for ( let i = 0; i < 10; i++ ) {
this.list.push(i)
}
}
}
```
#垂直滑动
```html
<yingbing-flip vertical :data="list" style="height: 100vh;">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<!-- #ifdef MP -->
<template v-for="(item, index) in list" :slot="index">
<!-- #endif -->
<view style="height: 100%">
<text style="font-size: 50px;font-weight: bold;color: #fff;">{{item}}</text>
</view>
</template>
</yingbing-flip>
```
```javascript
export default {
data () {
return {
list: []
}
},
onReady () {
for ( let i = 0; i < 10; i++ ) {
this.list.push(i)
}
}
}
```
#下拉刷新
```html
<yingbing-flip :data="list" vertical pulldownable @pulldown="handlePulldown" style="height: 100vh;">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<!-- #ifdef MP -->
<template v-for="(item, index) in list" :slot="index">
<!-- #endif -->
<view style="height: 100%">
<text style="font-size: 50px;font-weight: bold;color: #fff;">{{item}}</text>
</view>
</template>
<template #pulldownDefault>
<view class="pulldown">
<text>下拉刷新</text>
</view>
</template>
<template #pulldownReady>
<view class="pulldown">
<text>松开刷新</text>
</view>
</template>
<template #pulldownLoading>
<view class="pulldown">
<text>正在刷新</text>
</view>
</template>
<template #pulldownSuccess>
<view class="pulldown">
<text>刷新成功</text>
</view>
</template>
<template #pulldownFail>
<view class="pulldown">
<text>刷新失败</text>
</view>
</template>
</yingbing-flip>
```
```javascript
export default {
data () {
return {
list: []
}
},
onReady () {
for ( let i = 0; i < 10; i++ ) {
this.list.push(i)
}
},
methods: {
handlePulldown (callback) {
//模拟请求
setTimeout(() => {
let arr = []
for ( let i = 0; i < 10; i++ ) {
arr.push(i)
}
this.list = arr
callback('success') //成功回调
// callback('fail') //失败回调
}, 500)
}
}
}
```
```css
.pulldown {
display: flex;
align-items: center;
justify-content: center;
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
}
```
#上拉加载
```html
<yingbing-flip :data="list" vertical pullupable @pullup="handlePullup" style="height: 100vh;">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<!-- #ifdef MP -->
<template v-for="(item, index) in list" :slot="index">
<!-- #endif -->
<view style="height: 100%">
<text style="font-size: 50px;font-weight: bold;color: #fff;">{{item}}</text>
</view>
</template>
<template #pullupDefault>
<view class="pulldown">
<text>上拉加载</text>
</view>
</template>
<template #pullupReady>
<view class="pulldown">
<text>松开刷新</text>
</view>
</template>
<template #pullupLoading>
<view class="pulldown">
<text>正在刷新</text>
</view>
</template>
<template #pullupSuccess>
<view class="pulldown">
<text>刷新成功</text>
</view>
</template>
<template #pullupFail>
<view class="pulldown">
<text>刷新失败</text>
</view>
</template>
</yingbing-flip>
```
```javascript
export default {
data () {
return {
list: []
}
},
onReady () {
for ( let i = 0; i < 10; i++ ) {
this.list.push(i)
}
},
methods: {
handlePullup (callback) {
//模拟请求
setTimeout(() => {
for ( let i = 0; i < 10; i++ ) {
this.list.push(i)
}
callback('success') //成功回调
// callback('fail') //失败回调
}, 500)
}
}
}
```
```css
.pulldown {
display: flex;
align-items: center;
justify-content: center;
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
}
```
#JS控制滑动
```html
<yingbing-flip ref="flip" :data="list" style="height: 100vh;">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<!-- #ifdef MP -->
<template v-for="(item, index) in list" :slot="index">
<!-- #endif -->
<view style="height: 100%">
<text style="font-size: 50px;font-weight: bold;color: #fff;">{{item}}</text>
</view>
</template>
</yingbing-flip>
<button @tap="flipToPrev">向前滑动</button>
<button @tap="flipToNext">向后滑动</button>
```
```javascript
export default {
data () {
return {
list: [1,2,3,5,6]
}
},
methods: {
flipToNext () {
this.$refs.flip.flipToNext()
},
flipToPrev () {
this.$refs.flip.flipToPrev()
}
}
}
```
#刷新组件
```html
<yingbing-flip ref="flip" :data="list" style="height: 100vh;">
<!-- #ifndef MP -->
<template v-slot="{item, index}">
<!-- #endif -->
<!-- #ifdef MP -->
<template v-for="(item, index) in list" :slot="index">
<!-- #endif -->
<view style="height: 100%">
<text style="font-size: 50px;font-weight: bold;color: #fff;">{{item}}</text>
</view>
</template>
</yingbing-flip>
<button @tap="refresh">刷新</button>
```
```javascript
export default {
data () {
return {
list: [1,2,3,5,6]
}
},
methods: {
refresh () {
this.list = [7,8,9,10,11]
this.$refs.flip.refresh()
}
}
}
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long