最近要做大圖預(yù)覽功能媳否,這就繞不開大圖縮放奈应,為此花了點(diǎn)時間做這個功能
具體功能點(diǎn)如下:
1:雙指捏合圖片放大和縮小
2:雙擊放大和縮小
3:放大后的單指拖動和松開手指后的慣性滑動
4:可與輪播圖共用
遇到的主要問題點(diǎn)如下:
1:圖片怎么縮放
2:圖片怎么滑動
3:縮放后滑動時的邊界怎么處理
4:與輪播圖的滑動事件沖突
5:怎么在手指松開之后判斷是否進(jìn)行慣性滑動
6:怎么進(jìn)行慣性滑動
7:放大后松開手指時怎么不讓圖片滑動
8:怎么監(jiān)聽雙擊事件 ?
問題解答:
1:圖片縮放
使用Image的 transform() 進(jìn)行縮放叠聋,具體捏合手勢通過PinchGesture管理
2:圖片滑動
使用Image的 offset() 進(jìn)行滑動柒爸,具體滑動手勢通過PanGesture管理
3:邊界處理
在圖片縮放的過程中實(shí)時計算出X軸和Y軸的最大滑動距離,大概思路如下:
// PinchGesture的onActionUpdate()方法
this.maxScrollX = (this.imageWidth * this.scaleInfo.scaleValue - this.screenWidth) / 2
this.maxScrollY = (this.imageHeight * this.scaleInfo.scaleValue - this.screenHeight) / 2
if (this.maxScrollX < 0){
this.maxScrollX = 0
}
if (this.maxScrollY < 0){
this.maxScrollY = 0
}
圖片放大后滑動到上下左右的某個邊界西傀,然后進(jìn)行縮小斤寇,縮小時會滑出邊界并且顯露出父組件的背景色,這時就需要邊縮小邊對圖片進(jìn)行移動——移動到邊界處拥褂,具體思路如下:
// PinchGesture的onActionUpdate()方法
if (event.scale < 1) { //縮小
if (this.offsetInfo.currentX > this.maxScrollX) {
this.offsetInfo.currentX = this.maxScrollX
}
if (this.offsetInfo.currentX < -this.maxScrollX) {
this.offsetInfo.currentX = -this.maxScrollX
}
if (this.offsetInfo.currentY > this.maxScrollY) {
this.offsetInfo.currentY = this.maxScrollY
}
if (this.offsetInfo.currentY < -this.maxScrollY) {
this.offsetInfo.currentY = -this.maxScrollY
}
}
4:與輪播圖的滑動事件沖突
圖片在輪播圖中放大之后娘锁,本來只是想滑動圖片的,但是卻進(jìn)行了輪播圖的換頁肿仑,從這里看是圖片的滑動事件和輪播圖的滑動事件沖突了致盟,解決方法如下:
首先通過Swiper的disableSwipe()方法碎税,禁用輪播圖自帶的切換功能尤慰,然后在圖片滑動時進(jìn)行控制,在圖片滑動到左右邊界的時候雷蹂,通過SwiperController來進(jìn)行上一頁伟端、下一頁的操作,這樣會有切換的動畫匪煌,切記不要直接操作Swiper的index()方法责蝠,這樣會導(dǎo)致沒有切換動畫
swiperController.showNext()
swiperController.showPrevious()
圖片滑動到邊界時直接切換輪播圖的下一頁或者上一頁,就會導(dǎo)致滑動的時候很容易直接切換了輪播圖萎庭,但是我們此時還是想看當(dāng)前圖片霜医,因此需要滑動到邊界之后進(jìn)行二次滑動,再進(jìn)行輪播圖的翻頁驳规,通過增加isSliding這個boolean值進(jìn)行判斷肴敛,圖片滑動時設(shè)置為true,在PanGesture的onActionEnd()中設(shè)置為false——注意這里需要延遲設(shè)置為false,只有為false的時候才允許切換輪播圖
//滑動圖片時進(jìn)行輪播圖翻頁
onSlidPic(event: GestureEvent){
if (this.isSliding == false) { //一次滑動只允許翻一頁
if (Math.abs(event.offsetX) > this.mMinimumSlideSwiper){ //必須大于最小滑動距離医男,才能翻頁
if (this.onSlideSwiper != null){
if (event.offsetX > 0) { //右滑
this.onSlideSwiper(false)
}
if (event.offsetX < 0) { //左滑
this.onSlideSwiper(true)
}
}
this.isSliding = true
}
}
}
5:怎么在手指松開之后判斷是否進(jìn)行慣性滑動
首先需要明確進(jìn)行慣性滑動的時機(jī)為PanGesture的onActionEnd()方法砸狞,這里是手指滑動操作結(jié)束的標(biāo)志;如果直接在這里進(jìn)行慣性滑動話镀梭,在手指滑動很慢然后抬起的情況下也會進(jìn)行慣性滑動刀森,這不符合操作習(xí)慣,所以就需要判斷手指抬起時在屏幕上的滑動速度报账,通過event.velocityX 和 event.velocityY我們可以獲取到手指抬起時Image在X軸和Y軸的滑動速度研底,當(dāng)著兩個值中的一個大于最小滑動速度時,我們就進(jìn)行慣性滑動
if (Math.max(Math.abs(event.velocityX), Math.abs(event.velocityY)) > this.mMinimumVelocity){
//進(jìn)行慣性滑動
}
6:怎么進(jìn)行慣性滑動
鴻蒙的官方開發(fā)文檔中有慣性滑動的方法笙什,但是效果不甚理想飘哨,首先是只能滑動event.offsetX的距離,這就導(dǎo)致滑動距離很短琐凭;其次我建議使用EaseOut作為滑動效果芽隆,因?yàn)槲覀兪侨较蚧瑒樱换瑒拥膭赢嫊r長建議設(shè)置成300毫秒统屈;滑動距離我們使用event.offsetX * 15 和 event.offsetY * 15胚吁,這樣滑動距離長,效果更好愁憔;非慣性滑動時還是滑動event.offsetX和event.offsetY腕扶,這里注意區(qū)分。
具體滑動代碼示例如下:
//PanGesture的onActionEnd()方法中
//慣性滑動動畫:當(dāng)手指抬起時執(zhí)行吨掌,手指滑動過程中不執(zhí)行
if (Math.max(Math.abs(event.velocityX), Math.abs(event.velocityY)) > this.mMinimumVelocity){
animateTo({
duration: 300,
curve: Curve.EaseOut, //表示動畫以低速結(jié)束
iterations: 1 ,
playMode: PlayMode.Normal,
onFinish: () => {
//動畫結(jié)束100毫秒之后再保存移動距離半抱,立即保存的話會產(chǎn)生動畫卡頓
setTimeout(() => {
this.offsetInfo.stash()
this.isSliding = false
}, 100)
}
}, () => {
this.setOffSet(event,true)
})
}else {
this.offsetInfo.stash() //動畫結(jié)束
}
7:放大后松開手指時怎么不讓圖片滑動
雙指對圖片進(jìn)行縮放完松開手指時,因?yàn)槭种鸽x開屏幕時有先后順序可能會觸發(fā)滑動事件膜宋,導(dǎo)致圖片的突然移動窿侈,因此我在PinchGesture的onActionEnd()方法中加了一個延遲以控制是否正在捏合操作的變量,示例代碼如下:
setTimeout(() => {
this.isScaling = false //縮放完成150毫秒后再重置秋茫,防止剛縮放完手指離開屏幕時有先后順序?qū)е碌幕瑒? }, 150)
8:怎么監(jiān)聽雙擊事件 史简?
這個很簡單,直接上代碼:
//雙擊
TapGesture({ count: 2 })
.onAction((event: GestureEvent) => {
this.onDoubleClick()
})
完整代碼如下:
import { CustomImage } from './customImage/CustomImage';
import { CustomSwiperDataSourceObject, CustomSwiper } from './customImage/CustomSwiper';
/**
* 圖片預(yù)覽組件
*/
@Component
export struct CustomImagePreview {
@Link show: boolean;
@Require @Prop imageList: string[]; //預(yù)覽圖片列表
@Require @Prop currentIndex: number; //當(dāng)前預(yù)覽第幾張圖
@State private swiperList: CustomSwiperDataSourceObject<string>[] = [];
private swiperController: SwiperController = new SwiperController() //輪播圖翻頁控制器
aboutToAppear(): void {
this.swiperList = this.imageList.map((item: string, _index: number) => {
let data: CustomSwiperDataSourceObject<string> = {
swiperItem: item
}
return data
})
}
@Builder
imageBuilder(item: CustomSwiperDataSourceObject<string>, index: number) {
Column() {
CustomImage({
imgUrl: item.swiperItem,
imgWidth: `100%`,
imgHeight: `100%`,
download: true,
imgBg: '#00000000',
canScal: true, //是否可以縮放肛著,需要縮放時配置
onSlideSwiper: (isNextPage: boolean) => {
if (isNextPage) {
this.swiperController.showNext()
} else {
this.swiperController.showPrevious()
}
}
})
.onClick(() => {
this.show = false
})
}
}
build() {
Stack() {
Row() {
Text(`${this.currentIndex + 1}/${this.imageList.length}`)
.fontSize(vp2px(8))
.fontColor('#FFFFFF')
.textAlign(TextAlign.Center)
}
.width(`100%`)
.height(vp2px(10))
.position({
y: vp2px(0)
})
.zIndex(10)
.justifyContent(FlexAlign.Center)
Column() {
CustomSwiper({
swiperList: this.swiperList,
index: this.currentIndex,
// showIndicator: false,
autoPlay: false,
radius: 0,
swiperController: this.swiperController,
disableSwipe: true, //有圖片縮放功能時圆兵,必須禁用組件滑動功能, 傳值 true:禁用 false:啟用
itemBuilder: (item: CustomSwiperDataSourceObject<string>, index: number) => {
this.imageBuilder(item, index);
},
onSwiperChange: (item: CustomSwiperDataSourceObject<ESObject>, index: number) => {
this.currentIndex = index;
}
})
}.width(`100%`)
.height(`100%`)
.justifyContent(FlexAlign.Center)
.zIndex(9)
}.width(`100%`)
.height(`100%`)
.backgroundColor('#000000')
}
}
import { CommonDataSource } from '../common/CommonDataSource';
/**
* 通用輪播組件
* @param swiperList 輪播列表
* @param swiperHeight 輪播區(qū)域高度
* @param radius 圓角
*/
@Preview
@Component
export struct CustomSwiper {
// swiperList規(guī)定了 swiper 對象
@Prop swiperList: CustomSwiperDataSourceObject<ESObject>[];
// 當(dāng)前在容器中顯示的子組件的索引值枢贿。開始
@Prop index: number = 0;
// 是否自動播放殉农。
@Prop autoPlay: boolean = true;
// 自動播放時播放的時間間隔
@Prop interval: number = 3000;
// 是否開啟循環(huán)。
@Prop loop: boolean = true;
// 子組件切換的動畫時長局荚,單位為毫秒
@Prop duration: number = 400;
// 縱向滑動超凳。
@Prop vertical: boolean = false;
// 子組件與子組件之間間隙。
@Prop itemSpace: number = 0;
//設(shè)置Swiper的動畫曲線
@Prop curve: Curve | string | ICurve = Curve.Linear;
// 預(yù)加載子組件個數(shù)。
@Prop cachedCount: number = 0;
// 后邊距聪建,用于露出后一項(xiàng)的一小部分钙畔。
@Prop nextMargin: number = 0;
// 前邊距,用于露出前一項(xiàng)的一小部分
@Prop prevMargin: number = 0;
// 整個 swiper圓角
@Prop radius: BorderRadiuses | Length = vp2px(8)
// 是否禁用組件滑動切換功能
@Prop disableSwipe: boolean = false
// 內(nèi)容 builder
@Require @BuilderParam itemBuilder: (item: CustomSwiperDataSourceObject<ESObject>, index: number) => void
@State currentIndex: number = 0
// swiper 控制器
private swiperController: SwiperController = new SwiperController()
// swiper 數(shù)據(jù)
private data: CommonDataSource<CustomSwiperDataSourceObject<ESObject>> = new CommonDataSource([])
onSwiperChange?: (item: CustomSwiperDataSourceObject<ESObject>, index: number) => void
aboutToAppear(): void {
this.data = new CommonDataSource(this.swiperList)
}
build() {
Stack({
alignContent: Alignment.BottomEnd
}) {
Swiper(this.swiperController) {
LazyForEach(this.data, (item: CustomSwiperDataSourceObject<ESObject>, index: number) => {
this.itemBuilder(item, index);
}, (item: string) => item)
}
.index(this.index)
.autoPlay(this.autoPlay)
.interval(this.interval)
.loop(this.loop)
.duration(this.duration)
.vertical(this.vertical)
.itemSpace(this.itemSpace)
.cachedCount(this.cachedCount)
.curve(this.curve)
.nextMargin(this.nextMargin)
.prevMargin(this.prevMargin)
.borderRadius(this.radius)
.disableSwipe(this.disableSwipe)
.indicator(false) //不展示指示器
.onChange((_index: number) => {
this.currentIndex = _index;
if (this.onSwiperChange) {
this.onSwiperChange(this.swiperList[_index], _index)
}
})
}
.width('100%')
}
}
export interface CustomSwiperDataSourceObject<T> {
swiperItem: T;
}
import { display, matrix4 } from '@kit.ArkUI';
import { CustomScaleModel } from './CustomScaleModel';
import { CustomOffsetModel } from './CustomOffsetModel';
// 圖片信息
interface imgInfo {
width: number;
height: number;
componentWidth: number;
componentHeight: number;
loadingStatus: number;
contentWidth: number;
contentHeight: number;
contentOffsetX: number;
contentOffsetY: number;
}
/**
* KImage 圖片組件
* @param imgUrl: PixelMap | ResourceStr | DrawableDescriptor
* @param imgWidth
* @param imgHeight
* @param imgAlt
* @param imgBorderRadius
* @param imgFit
* @param imgBg
* @callback onCompleteCall
* @callback onErrorCall
* */
@Component
export struct CustomImage {
// url
@Require @Prop imgUrl: PixelMap | ResourceStr | DrawableDescriptor
// 寬
@Prop imgWidth: Length
// 高
@Prop imgHeight: Length
//加載時的占位圖
@Prop imgAlt: string | Resource = $r('app.media.imgError')
@Prop hasImgAlt: boolean = true
// 是否支持長按下載金麸,默認(rèn)不支持
@Prop download: boolean = true
// 圓角
@Prop imgBorderRadius: Length | BorderRadiuses
// ImageFit
@Prop imgFit: ImageFit = ImageFit.Contain
// 正常加載圖片時的背景色擎析,默認(rèn)為 bg_lightgray4
@Prop imgBg: ResourceColor = "#F0F0F0"
// 加載圖片異常時的背景色,按需配置
@Prop imgBgError: ResourceColor = "#F0F0F0"
// 圖片寬高比
@Prop imgAspectRatio: number
@Prop imgBorder: BorderOptions | null
// 圖片加載成功
onCompleteCall?: (imgInfo?: imgInfo) => void
// 圖片加載失敗
onErrorCall?: (error: ImageError) => void
// 加載失敗
@State isError: Boolean = false
// 加載中
@State isLoading: Boolean = true
//============== 圖片縮放相關(guān) ==============
private screenWidth: number = 0
private screenHeight: number = 0
private imageWidth: number = 0
private imageHeight: number = 0
private maxScrollX: number = 0 //x軸最大移動距離
private maxScrollY: number = 0 //y軸最大移動距離
private onSlideSwiper?: (isNextPage: boolean) => void //輪播圖切換
private mMinimumVelocity: number = 50 //手指抬起時可以進(jìn)行慣性滑動的最小速度
private mMinimumSlideSwiper: number = 20 //手指抬起時可以進(jìn)行慣性滑動的最小速度
private canScal: boolean = false //是否可以縮放
@State scaleInfo: CustomScaleModel = new CustomScaleModel(1.0, 1.0, 5.0)
@State offsetInfo: CustomOffsetModel = new CustomOffsetModel(0, 0)
@State matrix: matrix4.Matrix4Transit = matrix4.identity().copy()
@State isScaling: boolean = false //是否正在縮放挥下,正在縮放時不允許滑動圖片
@State isSliding: boolean = false //是否正在滑動
aboutToAppear(): void {
this.screenWidth = px2vp(display.getDefaultDisplaySync().width)
this.screenHeight = px2vp(display.getDefaultDisplaySync().height)
}
build() {
Stack() {
this.showImage()
if (this.isLoading || this.isError) {
if (this.hasImgAlt) {
this.showKfzLogo()
}
}
}
.width(this.imgWidth)
.height(this.imgHeight)
.clip(true)
.borderRadius(this.imgBorderRadius)
.backgroundColor(this.isError ? this.imgBgError : this.imgBg)
.alignContent(Alignment.Center)
}
// 占位圖
@Builder
showKfzLogo() {
Image(this.imgAlt)
.fillColor("#FFFFFF")
.objectFit(ImageFit.Contain)
.width(typeof this.imgWidth === 'number' ? (this.imgWidth * 0.6) : '60%')
.height(typeof this.imgHeight === 'number' ? (this.imgHeight * 0.6) : '60%')
.aspectRatio(this.imgAspectRatio ?? null)
}
// 圖片
@Builder
showImage() {
Image(this.imgUrl)
.width(this.imgWidth)
.height(this.imgHeight)
.objectFit(this.imgFit)
.aspectRatio(this.imgAspectRatio)
.border(this.imgBorder)
.borderRadius(this.imgBorderRadius)
.onComplete((imgInfo) => {
this.isLoading = false
this.isError = false
if (imgInfo) {
this.imageWidth = px2vp(imgInfo.contentWidth)
//得到圖片繪制的高度 單位 vp的
this.imageHeight = px2vp(imgInfo.contentHeight)
}
if (this.onCompleteCall) {
this.onCompleteCall(imgInfo)
}
})
.onError((error) => {
this.isLoading = false
this.isError = true
if (this.onErrorCall) {
this.onErrorCall(error)
}
})
.transform(this.canScal ? this.matrix : null)// 通過matrix控制圖片的縮放
.offset({
//通過offset控制圖片的偏移
x: this.canScal ? this.offsetInfo.currentX : 0,
y: this.canScal ? this.offsetInfo.currentY : 0
})
.onImageGesture()
.onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => {
//捏合手勢揍魂、滑動手勢在非看大圖模式下不響應(yīng)
if ((gestureInfo.type == GestureControl.GestureType.PAN_GESTURE ||
gestureInfo.type == GestureControl.GestureType.PINCH_GESTURE) && !this.canScal) {
return GestureJudgeResult.REJECT
}
return GestureJudgeResult.CONTINUE
})
}
@Styles
onImageGesture(){
.gesture(
GestureGroup(GestureMode.Parallel,
//手指縮放
PinchGesture({ fingers: 2, distance: 1 })
.onActionUpdate((event: GestureEvent) => {
this.onPinchGestureUpDate(event)
})
.onActionEnd((event: GestureEvent) => {
this.scaleInfo.stash()
setTimeout(() => {
this.isScaling = false //縮放完成150毫秒后再重置,防止剛縮放完手指離開屏幕時有先后順序?qū)е碌幕瑒? }, 150)
}),
//滑動圖片
PanGesture({ fingers: 1, direction: PanDirection.All })
.onActionUpdate((event: GestureEvent) => {
this.onPanGestureUpDate(event)
})
.onActionEnd((event: GestureEvent) => {
if (this.isScaling) {
return
}
//慣性滑動動畫:當(dāng)手指抬起時執(zhí)行棚瘟,手指滑動過程中不執(zhí)行
if (Math.max(Math.abs(event.velocityX), Math.abs(event.velocityY)) > this.mMinimumVelocity) {
animateTo({
duration: 300,
curve: Curve.EaseOut, //表示動畫以低速結(jié)束
iterations: 1,
playMode: PlayMode.Normal,
onFinish: () => {
//動畫結(jié)束100毫秒之后再保存移動距離现斋,立即保存的話會產(chǎn)生動畫卡頓
setTimeout(() => {
this.offsetInfo.stash()
this.isSliding = false
}, 100)
}
}, () => {
this.setOffSet(event, true)
})
} else {
this.offsetInfo.stash() //動畫結(jié)束
}
}),
//雙擊
TapGesture({ count: 2 })
.onAction((event: GestureEvent) => {
this.onDoubleClick()
})
)
)
}
onDoubleClick() {
if (this.scaleInfo.scaleValue > this.scaleInfo.minScaleValue) {
//雙擊時已經(jīng)放大了
this.scaleInfo.scaleValue = this.scaleInfo.minScaleValue
} else {
this.scaleInfo.scaleValue = this.scaleInfo.mediumScaleValue
}
this.scaleInfo.lastValue = this.scaleInfo.scaleValue
//matrix默認(rèn)縮放中心為組件中心
this.matrix = matrix4.identity().scale({
x: this.scaleInfo.scaleValue,
y: this.scaleInfo.scaleValue,
}).copy()
}
//手勢縮放
onPinchGestureUpDate(event: GestureEvent) {
this.isScaling = true
this.scaleInfo.scaleValue = this.scaleInfo.lastValue * event.scale
// 縮放時不允許大于最大縮放,不允許小于默認(rèn)大小
if (this.scaleInfo.scaleValue > this.scaleInfo.maxScaleValue) {
this.scaleInfo.scaleValue = this.scaleInfo.maxScaleValue
}
if (this.scaleInfo.scaleValue < this.scaleInfo.minScaleValue) {
this.scaleInfo.scaleValue = this.scaleInfo.minScaleValue
}
//matrix默認(rèn)縮放中心為組件中心
this.matrix = matrix4.identity().scale({
x: this.scaleInfo.scaleValue,
y: this.scaleInfo.scaleValue,
}).copy()
//計算X軸/Y軸最大移動距離
this.maxScrollX = (this.imageWidth * this.scaleInfo.scaleValue - this.screenWidth) / 2
this.maxScrollY = (this.imageHeight * this.scaleInfo.scaleValue - this.screenHeight) / 2
if (this.maxScrollX < 0) {
this.maxScrollX = 0
}
if (this.maxScrollY < 0) {
this.maxScrollY = 0
}
if (event.scale < 1) { //縮小
if (this.offsetInfo.currentX > this.maxScrollX) {
this.offsetInfo.currentX = this.maxScrollX
}
if (this.offsetInfo.currentX < -this.maxScrollX) {
this.offsetInfo.currentX = -this.maxScrollX
}
if (this.offsetInfo.currentY > this.maxScrollY) {
this.offsetInfo.currentY = this.maxScrollY
}
if (this.offsetInfo.currentY < -this.maxScrollY) {
this.offsetInfo.currentY = -this.maxScrollY
}
}
}
//手勢滑動
onPanGestureUpDate(event: GestureEvent) {
//正在縮放偎蘸,不允許移動
if (this.isScaling) {
return
}
if (this.scaleInfo.scaleValue === this.scaleInfo.minScaleValue) {
this.onSlidPic(event) //還未縮放就進(jìn)行滑動庄蹋,則輪播圖翻頁
} else {
this.setOffSet(event, false)
}
}
//設(shè)置圖片偏移量
setOffSet(event: GestureEvent, isFly: boolean) {
//滑動到邊界之后進(jìn)行二次滑動,則輪播圖翻頁:注意這里需要區(qū)分左右兩個邊界的單獨(dú)滑動
if (this.offsetInfo.currentX == this.maxScrollX && event.offsetX > 0) {
this.onSlidPic(event)
return
}
if (this.offsetInfo.currentX == -this.maxScrollX && event.offsetX < 0) {
this.onSlidPic(event)
return
}
if (this.maxScrollX >= 0) { //縮放后圖片寬度大于屏幕寬度
//X軸移動
let preScrollX = 0
if (isFly) {
preScrollX = this.offsetInfo.lastX + event.offsetX * 15 //預(yù)計X軸要移動的距離
} else {
preScrollX = this.offsetInfo.lastX + event.offsetX //預(yù)計X軸要移動的距離
}
if (Math.abs(preScrollX) < this.maxScrollX) {
this.offsetInfo.currentX = preScrollX
} else {
if (preScrollX < 0) {
this.offsetInfo.currentX = -this.maxScrollX
} else {
this.offsetInfo.currentX = this.maxScrollX
}
}
this.isSliding = true
}
//Y軸移動
if (this.maxScrollY >= 0) { //縮放后圖片高度大于屏幕高度
let preScrollY = 0
if (isFly) {
preScrollY = this.offsetInfo.lastY + event.offsetY * 15 //預(yù)計Y軸要移動的距離
} else {
preScrollY = this.offsetInfo.lastY + event.offsetY //預(yù)計Y軸要移動的距離
}
if (Math.abs(preScrollY) < this.maxScrollY) {
this.offsetInfo.currentY = preScrollY
} else {
if (preScrollY < 0) {
this.offsetInfo.currentY = -this.maxScrollY
} else {
this.offsetInfo.currentY = this.maxScrollY
}
}
this.isSliding = true
}
}
//滑動圖片時進(jìn)行輪播圖翻頁
onSlidPic(event: GestureEvent) {
if (this.isSliding == false) { //一次滑動只允許翻一頁
if (Math.abs(event.offsetX) > this.mMinimumSlideSwiper) { //必須大于最小滑動距離迷雪,才能翻頁
if (this.onSlideSwiper != null) {
if (event.offsetX > 0) { //右滑
this.onSlideSwiper(false)
}
if (event.offsetX < 0) { //左滑
this.onSlideSwiper(true)
}
}
this.isSliding = true
}
}
}
}
@Observed
export class CustomOffsetModel {
public currentX: number
public currentY: number
public lastX: number = 0
public lastY: number = 0
constructor(currentX: number = 0, currentY: number = 0) {
this.currentX = currentX;
this.currentY = currentY;
}
reset(): void {
this.currentX = 0
this.currentY = 0
this.lastX = 0
this.lastY = 0
}
stash(): void {
this.lastX = this.currentX
this.lastY = this.currentY
}
}
@Observed
export class CustomScaleModel {
public scaleValue: number //本次縮放值
public lastValue: number //記錄上次縮放完后的縮放值
public maxScaleValue: number //最大默認(rèn)縮放值
public minScaleValue: number = 1 //最小縮放值
public mediumScaleValue: number = 2.5 //雙擊的放大值
constructor(scaleValue: number = 1.0, lastValue: number = 1.0, maxScaleValue: number = 5,
mediumScaleValue: number = 2.5) {
this.scaleValue = scaleValue
this.lastValue = lastValue
this.maxScaleValue = maxScaleValue
this.mediumScaleValue = mediumScaleValue
}
reset(): void {
this.scaleValue = this.minScaleValue
this.lastValue = this.scaleValue
}
stash(): void {
this.lastValue = this.scaleValue
}
}
@Observed
export class ObservedArray<T> extends Array<T> {
constructor(args?: T[]) {
if (args instanceof Array) {
super(...args);
} else {
super();
}
}
}
@Observed
export class CommonDataSource<T> implements IDataSource {
private dataArray: T[] = [];
private listeners: DataChangeListener[] = [];
constructor(element: T[]) {
this.dataArray = element;
}
public getData(index: number) {
return this.dataArray[index]
}
public totalCount(): number {
return this.dataArray.length;
}
public addData(index: number, data: T): void {
this.dataArray.splice(index, 0, data);
this.notifyDataAdd(index);
}
public addDatas(index: number, datas: T[]): void {
this.dataArray = this.dataArray.concat(datas);
this.notifyDataAdd(index);
}
public pushAllData(newData: ObservedArray<T>): void {
this.clear();
this.dataArray.push(...newData);
this.notifyDataReload();
}
//分頁加載時不要使用限书,會重新加載所有數(shù)據(jù)導(dǎo)致卡頓
public appendAllData(addData: ObservedArray<T>): void {
this.dataArray.push(...addData);
this.notifyDataReload()
}
//分頁加載時不要使用,會重新加載所有數(shù)據(jù)導(dǎo)致卡頓
public addAllDatas(datas: T[]): void {
datas.forEach(data => {
this.dataArray.push(data);
})
this.notifyDataReload();
}
public setData(datas: T[]): void {
this.clear();
this.addAllDatas(datas);
}
public pushData(data: T): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
public deleteData(index: number): void {
this.dataArray.splice(index, 1);
this.notifyDataDelete(index)
}
public moveData(from: number, to: number): void {
let temp: T = this.dataArray[from];
this.dataArray[from] = this.dataArray[to];
this.dataArray[to] = temp;
this.notifyDataMove(from, to);
}
public changeData(index: number, data: T): void {
this.dataArray.splice(index, 1, data);
this.notifyDataChange(index);
}
public reloadData(): void {
this.notifyDataReload();
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
notifyDataReload(): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataMove(from, to);
})
}
public notifyDatasetChange(operations: DataOperation[]): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDatasetChange(operations);
})
}
getDataArray() {
return this.dataArray
}
getItemIndex(t: T): number {
return this.dataArray.indexOf(t)
}
public clear(): void {
this.dataArray.splice(0, this.dataArray?.length)
}
public deleteAll(): void {
while (this.dataArray.length > 0) {
this.deleteData(0)
}
}
}