HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

如何实现页面上滑tabBar和顶部标题的悬停以及属性动画效果

tabbar

问题现象
参照Scroll的示例3,实现了tabBar在页面顶部的悬停效果。

如果页面顶部还有一个标题栏(即下图中“首页”),现在要实现tab内容向上滚动时tabBar悬停到标题栏下方(即下图中“同城”,“推荐”,“活动”,“玩机”这一行),且可以实现标题栏在滚动过程中样式变化(如下图背景变化),该如何实现?

点击放大点击放大

背景知识
Stack:堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件。

Scroll:可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。

nestedScroll:设置前后两个方向的嵌套滚动模式,实现与父组件的滚动联动。
onDidScroll:滚动事件回调,Scroll滚动时触发。
解决方案
使用Stack层叠布局,将标题栏悬浮展示在页面顶部。
考虑页面滚动以及tabContent里面的list滚动,就要考虑滚动嵌套问题,目前场景需要选择:
向上滚动时:父组件先滚动,父组件滚动到边缘以后自身滚动;
向下滚动时:自身先滚动,自身滚动到边缘以后父组件滚动。
示例代码如下:

.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
});

父组件滚动过程中,根据滚动偏移量线性修改标题栏样式(如字体大小、透明度、背景等),onDidScroll可以返回当前帧滚动的偏移量和当前滚动状态。
示例代码如下:
@Entry
@Component
struct StickyNestedScroll {
@State arr: number[] = [];
@State opacityNum: number = 0;
@State curYOffset: number = 0;
@State currentIndex: number = 0;

aboutToAppear() {
for (let i = 0; i < 30; i++) {
this.arr.push(i);
}
}

@Styles
listCard() {
.backgroundColor('#FFF')
.height(64)
.width('calc(100% - 32vp)')
.borderRadius(20)
.margin({
left: 16,
right: 16,
});
}

@Builder
tabBuilder(title: string, targetIndex: number) {
Column() {
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#FFF' : '#000')
.fontSize(14)
.fontWeight(this.currentIndex === targetIndex ? 500 : 400);
}
.height(36)
.padding({
left: 16,
right: 16,
})
.margin({ left: 8 })
.borderRadius(20)
.backgroundColor(this.currentIndex === targetIndex ? '#0A59F7' : 'rgba(0, 0, 0, 0.05)')
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center);
}

build() {
Stack() {
Scroll() {
Column() {
Image($r('app.media.scrollTopbg')) // 图片资源需自行替换
.width('100%')
.height(300);
Tabs({ barPosition: BarPosition.Start }) {
ForEach(this.arr.slice(0, 6),
(item: number, index: number) => {
TabContent() {
List({ space: 16 }) {
ForEach(this.arr, (item1: number) => {
ListItem() {
Text('item' + item1)
.fontSize(16).fontWeight(400);
}.listCard();
}, (item1: string) => item1);
}.width('100%')
.edgeEffect(EdgeEffect.None)
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
});

            }.tabBar(this.tabBuilder(`标签${item + 1}`, index));  
          }, (item: number, index: number) => JSON.stringify(item) + index);  
      }  
      .onChange((index) => {  
        this.currentIndex = index;  
      })  
      .barMode(BarMode.Scrollable)  
      .barHeight(72)  
      .vertical(false)  
      .width('100%')  
      .height('calc(100% - 80vp)');  
    }.width('100%');  
  }  
  .friction(0.6)  
  .scrollBar(BarState.Off)  
  .width('100%')  
  .height('100%')  
  .onDidScroll((xOffset: number, yOffset: number, scrollState: ScrollState): void => {  
    // 累计计算当前父组件滚动在Y轴方向的偏移量  
    this.curYOffset += yOffset;  
    // 根据父组件一共可以滚动的距离计算当前每帧的当前透明度  
    let opacity = this.curYOffset / 220;  
    if (opacity >= 1) {  
      opacity = 1;  
    }  
    if (opacity <= 0) {  
      opacity = 0;  
    }  
    this.opacityNum = opacity;  
    console.info(`xOffset: ${xOffset},scrollState:${scrollState}`);  
  });  

  // 悬浮标题栏  
  Text('工作台')  
    .fontSize(24)  
    .fontColor('#000')  
    .fontWeight(FontWeight.Bold)  
    .backgroundColor(`rgba(255,255,255,${this.opacityNum})`)  
    .position({ x: 0, y: 0 })  
    .width('100%')  
    .height(80)  
    .padding({ left: 16, top: 44 });  
}.width('100%')  
.height('100%').background('#F1F3F5');  

}
}
效果如图:

点击放大

总结
悬浮类布局首先考虑Stack层叠布局。
嵌套滚动,考虑父子组件之间的先后滚动模式,选择合适的滚动模式。
滚动过程中,获取当前滚动每帧偏移量和滚动状态,做合适的UI更新。
https://pastebin.com/H9s5Prqc
https://pastebin.com/B3rdpE0P
https://pastebin.com/1DnHtpyd
https://pastebin.com/TEdBHtNb
https://pastebin.com/LJ0cQ7Uu
https://pastebin.com/bEc1mLCD
https://pastebin.com/kgSG3Gqx
https://pastebin.com/562Mtrb0
https://pastebin.com/TcJ1ucZM
https://pastebin.com/NdYhTKwU
https://pastebin.com/0kg2myHE
https://pastebin.com/tuXkKyRR
https://pastebin.com/JNaUA51y
https://pastebin.com/RqerbF2p
https://pastebin.com/jzeTmGu6
https://pastebin.com/bG1T2kG0
https://pastebin.com/2waPB0AK
https://pastebin.com/miLDPTFG
https://pastebin.com/2GVXUYr6
https://pastebin.com/RDfika4N
https://pastebin.com/yVTUxb9T
https://pastebin.com/8fpFzpMF
https://pastebin.com/dxrwgxZb
https://pastebin.com/WW5LdheP
https://pastebin.com/j9dwuCiX
https://pastebin.com/4pjqADUq
https://pastebin.com/RMeYiqCG
https://pastebin.com/jcEZ3mCw
https://pastebin.com/u0UKBHMc
https://pastebin.com/6AtALgcG
https://pastebin.com/yZZZgpRJ
https://pastebin.com/LbQCHdWp
https://pastebin.com/8uaaUxm3
https://pastebin.com/smKGwEkJ
https://pastebin.com/XZS8Lgc9
https://pastebin.com/PXPcxxu5
https://pastebin.com/AzHkpzSh
https://pastebin.com/kdDFUmsj
https://pastebin.com/ejETYeux
https://pastebin.com/s4Tj7hqX
https://pastebin.com/n8vn16hR
https://pastebin.com/cUBURDS5
https://pastebin.com/iHuTYWub
https://pastebin.com/it84K7XW
https://pastebin.com/dHhXdDc4
https://pastebin.com/btsEnW55
https://pastebin.com/VhQvwq6f
https://pastebin.com/prMTmsc5
https://pastebin.com/giVp1BpF
https://pastebin.com/fQyYpmGY
https://pastebin.com/gAg3F6UY
https://pastebin.com/2QWLCNvx
https://pastebin.com/2gxy4TKg
https://pastebin.com/xKYNRWMk
https://pastebin.com/1HxCD5DS
https://pastebin.com/GiAXtWj1
https://pastebin.com/m8afMq62
https://pastebin.com/55Xb4vqW
https://pastebin.com/zJcq0WY7
https://pastebin.com/WmVwfQrY
https://pastebin.com/91bnWbQF
https://pastebin.com/gvKhZ9Tc
https://pastebin.com/Nt21BqQ7
https://pastebin.com/wM2aTQ0w
https://pastebin.com/Vc7vvzKJ
https://pastebin.com/yaq33eZQ
https://pastebin.com/qZ5F9NYb
https://pastebin.com/i3E4FgCp
https://pastebin.com/AkTA5RZZ
https://pastebin.com/ELEG1B0m
https://pastebin.com/5JWdPFhR
https://pastebin.com/R0YTHUS4
https://pastebin.com/x9nRf1j0
https://pastebin.com/Aih0qsy2
https://pastebin.com/CpwvWE4e
https://pastebin.com/rr9rRU2E
https://pastebin.com/6Ye4Gwq7
https://pastebin.com/DAw6tKSy
https://pastebin.com/6gFkNdxP
https://pastebin.com/zMtnD7tM
https://pastebin.com/95F1ruf8
https://pastebin.com/ZXeMfgMs
https://pastebin.com/s8Xu4WDd
https://pastebin.com/cTfEk1wu
https://pastebin.com/Mjm4d4Rz
https://pastebin.com/gRAQUhkK
https://pastebin.com/YpNWSg6u
https://pastebin.com/51Edu6ba
https://pastebin.com/XkAuM8JZ
https://pastebin.com/vcnnDhb9
https://pastebin.com/hjhUzWgG
https://pastebin.com/Za2eji2y
https://pastebin.com/6HcM5ez2
https://pastebin.com/iKpg8iex
https://pastebin.com/9zDKuETK
https://pastebin.com/M4swhPLu
https://pastebin.com/BFs6X9UX
https://pastebin.com/GWjxjEgb
https://pastebin.com/E1LbLCgp
https://pastebin.com/Ze95QMAi
https://pastebin.com/ZY8zYNWn
https://pastebin.com/fTLHPLZZ
https://pastebin.com/6fDk9tYi
https://pastebin.com/GfuDHVfH
https://pastebin.com/KUq4BLce
https://pastebin.com/qY5k2ZxX
https://pastebin.com/8yXshjhD
https://pastebin.com/3BmddLGG
https://pastebin.com/6hgiPSQy
https://pastebin.com/pwCxCvBx
https://pastebin.com/rdgB20Hq
https://pastebin.com/fR1AXxN3
https://pastebin.com/XAyJJSh4
https://pastebin.com/Udz89NF9
https://pastebin.com/tRFNRTfq
https://pastebin.com/LYnbJkDX
https://pastebin.com/UrndZZ4r
https://pastebin.com/UJ0cJu7W
https://pastebin.com/VC9ftumY
https://pastebin.com/1mD57fxS
https://pastebin.com/4yCvSzPt
https://pastebin.com/Tds1KK8q
https://pastebin.com/cuTV0ANH
https://pastebin.com/tfvAj5zG
https://pastebin.com/YmTJJ9P4
https://pastebin.com/qr720c8a
https://pastebin.com/Qzx9z0jT
https://pastebin.com/phurPJTs
https://pastebin.com/gHHTkHD3
https://pastebin.com/ZwnPCDTj
https://pastebin.com/0DSjtHAt
https://pastebin.com/TftyJMxx
https://pastebin.com/vBBJvgLm
https://pastebin.com/NQYakApj
https://pastebin.com/meCbWSmb
https://pastebin.com/1VP7BDiy
https://pastebin.com/9dwnBKrH
https://pastebin.com/CfNxq9BD
https://pastebin.com/7SdTHhbB
https://pastebin.com/wjGatMb0
https://pastebin.com/Gv2kQv0K
https://pastebin.com/kqY5ZC2U
https://pastebin.com/NVQSYDC2
https://pastebin.com/p5cmmLTs
https://pastebin.com/ksUjWjEf
https://pastebin.com/iGqdf7JS
https://pastebin.com/1jPPW8SY
https://pastebin.com/zaiTAPGu
https://pastebin.com/ZyCEaVhf
https://pastebin.com/Z8nhRzGY
https://pastebin.com/4ANEqn0d
https://pastebin.com/B6aK9n6G
https://pastebin.com/0Tt4rGw1
https://pastebin.com/jV1WDDj1
https://pastebin.com/epsfQ9xR
https://pastebin.com/TDsuwVQq
https://pastebin.com/1sCfUZ1r
https://pastebin.com/rPZVVY9W
https://pastebin.com/YMAeatYs
https://pastebin.com/USZwiZLz
https://pastebin.com/0D4g5hpe
https://pastebin.com/aTgxSFAv
https://pastebin.com/d9yLduCs
https://pastebin.com/t2jGSt52
https://pastebin.com/ZCjZZ5cd
https://pastebin.com/fYBzqBQc
https://pastebin.com/rPSifbkZ
https://pastebin.com/r5hsmStB
https://pastebin.com/GBQJqUpA
https://pastebin.com/wi5TgU60
https://pastebin.com/17bmtrLm
https://pastebin.com/ymjx0ej6
https://pastebin.com/tnhPx5tx
https://pastebin.com/pKythJNa
https://pastebin.com/7rkBm8RR
https://pastebin.com/nKrgz0hx
https://pastebin.com/Ls4U30up
https://pastebin.com/GLrgZDKM
https://pastebin.com/QLenyQyb
https://pastebin.com/6kf7ctaD
https://pastebin.com/493xDX2E
https://pastebin.com/NNLBP1BA
https://pastebin.com/fQsBphT0
https://pastebin.com/iFUr5gqq
https://pastebin.com/YkZtGRGV
https://pastebin.com/CUgpDscE
https://pastebin.com/4BSRJRJ2
https://pastebin.com/rgaPPPXy
https://pastebin.com/e0MbXcLH
https://pastebin.com/0y661SPV
https://pastebin.com/WnhafHAL
https://pastebin.com/vdcuA6Wn
https://pastebin.com/0wP7iM13
https://pastebin.com/kSsbb7uN
https://pastebin.com/CKuHJUp0
https://pastebin.com/VFHuYY0w
https://pastebin.com/e57GMSyT
https://pastebin.com/JCJqt21y
https://pastebin.com/v03W6Wua
https://pastebin.com/Qh5cvmxD
https://pastebin.com/1n1rmrga
https://pastebin.com/EqpNrUd9
https://pastebin.com/hNzVc4VA
https://pastebin.com/wyd61PAX
https://pastebin.com/KUqAc0dA
https://pastebin.com/dAwvfQ3n
https://pastebin.com/tfhzjx0X
https://pastebin.com/qcry1nbc
https://pastebin.com/Xx1xKrr5
https://pastebin.com/z4RRXjbZ
https://pastebin.com/HFUs4bbZ
https://pastebin.com/7hFpzwBb
https://pastebin.com/kkxJEXkH
https://pastebin.com/8N244GbV
https://pastebin.com/6QgG0hC0
https://pastebin.com/SPfWYRk3
https://pastebin.com/CQQ6jn3J
https://pastebin.com/KHGYtVZu
https://pastebin.com/ibnJTTYm

继续阅读 »

问题现象
参照Scroll的示例3,实现了tabBar在页面顶部的悬停效果。

如果页面顶部还有一个标题栏(即下图中“首页”),现在要实现tab内容向上滚动时tabBar悬停到标题栏下方(即下图中“同城”,“推荐”,“活动”,“玩机”这一行),且可以实现标题栏在滚动过程中样式变化(如下图背景变化),该如何实现?

点击放大点击放大

背景知识
Stack:堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件。

Scroll:可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。

nestedScroll:设置前后两个方向的嵌套滚动模式,实现与父组件的滚动联动。
onDidScroll:滚动事件回调,Scroll滚动时触发。
解决方案
使用Stack层叠布局,将标题栏悬浮展示在页面顶部。
考虑页面滚动以及tabContent里面的list滚动,就要考虑滚动嵌套问题,目前场景需要选择:
向上滚动时:父组件先滚动,父组件滚动到边缘以后自身滚动;
向下滚动时:自身先滚动,自身滚动到边缘以后父组件滚动。
示例代码如下:

.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
});

父组件滚动过程中,根据滚动偏移量线性修改标题栏样式(如字体大小、透明度、背景等),onDidScroll可以返回当前帧滚动的偏移量和当前滚动状态。
示例代码如下:
@Entry
@Component
struct StickyNestedScroll {
@State arr: number[] = [];
@State opacityNum: number = 0;
@State curYOffset: number = 0;
@State currentIndex: number = 0;

aboutToAppear() {
for (let i = 0; i < 30; i++) {
this.arr.push(i);
}
}

@Styles
listCard() {
.backgroundColor('#FFF')
.height(64)
.width('calc(100% - 32vp)')
.borderRadius(20)
.margin({
left: 16,
right: 16,
});
}

@Builder
tabBuilder(title: string, targetIndex: number) {
Column() {
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#FFF' : '#000')
.fontSize(14)
.fontWeight(this.currentIndex === targetIndex ? 500 : 400);
}
.height(36)
.padding({
left: 16,
right: 16,
})
.margin({ left: 8 })
.borderRadius(20)
.backgroundColor(this.currentIndex === targetIndex ? '#0A59F7' : 'rgba(0, 0, 0, 0.05)')
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center);
}

build() {
Stack() {
Scroll() {
Column() {
Image($r('app.media.scrollTopbg')) // 图片资源需自行替换
.width('100%')
.height(300);
Tabs({ barPosition: BarPosition.Start }) {
ForEach(this.arr.slice(0, 6),
(item: number, index: number) => {
TabContent() {
List({ space: 16 }) {
ForEach(this.arr, (item1: number) => {
ListItem() {
Text('item' + item1)
.fontSize(16).fontWeight(400);
}.listCard();
}, (item1: string) => item1);
}.width('100%')
.edgeEffect(EdgeEffect.None)
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
});

            }.tabBar(this.tabBuilder(`标签${item + 1}`, index));  
          }, (item: number, index: number) => JSON.stringify(item) + index);  
      }  
      .onChange((index) => {  
        this.currentIndex = index;  
      })  
      .barMode(BarMode.Scrollable)  
      .barHeight(72)  
      .vertical(false)  
      .width('100%')  
      .height('calc(100% - 80vp)');  
    }.width('100%');  
  }  
  .friction(0.6)  
  .scrollBar(BarState.Off)  
  .width('100%')  
  .height('100%')  
  .onDidScroll((xOffset: number, yOffset: number, scrollState: ScrollState): void => {  
    // 累计计算当前父组件滚动在Y轴方向的偏移量  
    this.curYOffset += yOffset;  
    // 根据父组件一共可以滚动的距离计算当前每帧的当前透明度  
    let opacity = this.curYOffset / 220;  
    if (opacity >= 1) {  
      opacity = 1;  
    }  
    if (opacity <= 0) {  
      opacity = 0;  
    }  
    this.opacityNum = opacity;  
    console.info(`xOffset: ${xOffset},scrollState:${scrollState}`);  
  });  

  // 悬浮标题栏  
  Text('工作台')  
    .fontSize(24)  
    .fontColor('#000')  
    .fontWeight(FontWeight.Bold)  
    .backgroundColor(`rgba(255,255,255,${this.opacityNum})`)  
    .position({ x: 0, y: 0 })  
    .width('100%')  
    .height(80)  
    .padding({ left: 16, top: 44 });  
}.width('100%')  
.height('100%').background('#F1F3F5');  

}
}
效果如图:

点击放大

总结
悬浮类布局首先考虑Stack层叠布局。
嵌套滚动,考虑父子组件之间的先后滚动模式,选择合适的滚动模式。
滚动过程中,获取当前滚动每帧偏移量和滚动状态,做合适的UI更新。
https://pastebin.com/H9s5Prqc
https://pastebin.com/B3rdpE0P
https://pastebin.com/1DnHtpyd
https://pastebin.com/TEdBHtNb
https://pastebin.com/LJ0cQ7Uu
https://pastebin.com/bEc1mLCD
https://pastebin.com/kgSG3Gqx
https://pastebin.com/562Mtrb0
https://pastebin.com/TcJ1ucZM
https://pastebin.com/NdYhTKwU
https://pastebin.com/0kg2myHE
https://pastebin.com/tuXkKyRR
https://pastebin.com/JNaUA51y
https://pastebin.com/RqerbF2p
https://pastebin.com/jzeTmGu6
https://pastebin.com/bG1T2kG0
https://pastebin.com/2waPB0AK
https://pastebin.com/miLDPTFG
https://pastebin.com/2GVXUYr6
https://pastebin.com/RDfika4N
https://pastebin.com/yVTUxb9T
https://pastebin.com/8fpFzpMF
https://pastebin.com/dxrwgxZb
https://pastebin.com/WW5LdheP
https://pastebin.com/j9dwuCiX
https://pastebin.com/4pjqADUq
https://pastebin.com/RMeYiqCG
https://pastebin.com/jcEZ3mCw
https://pastebin.com/u0UKBHMc
https://pastebin.com/6AtALgcG
https://pastebin.com/yZZZgpRJ
https://pastebin.com/LbQCHdWp
https://pastebin.com/8uaaUxm3
https://pastebin.com/smKGwEkJ
https://pastebin.com/XZS8Lgc9
https://pastebin.com/PXPcxxu5
https://pastebin.com/AzHkpzSh
https://pastebin.com/kdDFUmsj
https://pastebin.com/ejETYeux
https://pastebin.com/s4Tj7hqX
https://pastebin.com/n8vn16hR
https://pastebin.com/cUBURDS5
https://pastebin.com/iHuTYWub
https://pastebin.com/it84K7XW
https://pastebin.com/dHhXdDc4
https://pastebin.com/btsEnW55
https://pastebin.com/VhQvwq6f
https://pastebin.com/prMTmsc5
https://pastebin.com/giVp1BpF
https://pastebin.com/fQyYpmGY
https://pastebin.com/gAg3F6UY
https://pastebin.com/2QWLCNvx
https://pastebin.com/2gxy4TKg
https://pastebin.com/xKYNRWMk
https://pastebin.com/1HxCD5DS
https://pastebin.com/GiAXtWj1
https://pastebin.com/m8afMq62
https://pastebin.com/55Xb4vqW
https://pastebin.com/zJcq0WY7
https://pastebin.com/WmVwfQrY
https://pastebin.com/91bnWbQF
https://pastebin.com/gvKhZ9Tc
https://pastebin.com/Nt21BqQ7
https://pastebin.com/wM2aTQ0w
https://pastebin.com/Vc7vvzKJ
https://pastebin.com/yaq33eZQ
https://pastebin.com/qZ5F9NYb
https://pastebin.com/i3E4FgCp
https://pastebin.com/AkTA5RZZ
https://pastebin.com/ELEG1B0m
https://pastebin.com/5JWdPFhR
https://pastebin.com/R0YTHUS4
https://pastebin.com/x9nRf1j0
https://pastebin.com/Aih0qsy2
https://pastebin.com/CpwvWE4e
https://pastebin.com/rr9rRU2E
https://pastebin.com/6Ye4Gwq7
https://pastebin.com/DAw6tKSy
https://pastebin.com/6gFkNdxP
https://pastebin.com/zMtnD7tM
https://pastebin.com/95F1ruf8
https://pastebin.com/ZXeMfgMs
https://pastebin.com/s8Xu4WDd
https://pastebin.com/cTfEk1wu
https://pastebin.com/Mjm4d4Rz
https://pastebin.com/gRAQUhkK
https://pastebin.com/YpNWSg6u
https://pastebin.com/51Edu6ba
https://pastebin.com/XkAuM8JZ
https://pastebin.com/vcnnDhb9
https://pastebin.com/hjhUzWgG
https://pastebin.com/Za2eji2y
https://pastebin.com/6HcM5ez2
https://pastebin.com/iKpg8iex
https://pastebin.com/9zDKuETK
https://pastebin.com/M4swhPLu
https://pastebin.com/BFs6X9UX
https://pastebin.com/GWjxjEgb
https://pastebin.com/E1LbLCgp
https://pastebin.com/Ze95QMAi
https://pastebin.com/ZY8zYNWn
https://pastebin.com/fTLHPLZZ
https://pastebin.com/6fDk9tYi
https://pastebin.com/GfuDHVfH
https://pastebin.com/KUq4BLce
https://pastebin.com/qY5k2ZxX
https://pastebin.com/8yXshjhD
https://pastebin.com/3BmddLGG
https://pastebin.com/6hgiPSQy
https://pastebin.com/pwCxCvBx
https://pastebin.com/rdgB20Hq
https://pastebin.com/fR1AXxN3
https://pastebin.com/XAyJJSh4
https://pastebin.com/Udz89NF9
https://pastebin.com/tRFNRTfq
https://pastebin.com/LYnbJkDX
https://pastebin.com/UrndZZ4r
https://pastebin.com/UJ0cJu7W
https://pastebin.com/VC9ftumY
https://pastebin.com/1mD57fxS
https://pastebin.com/4yCvSzPt
https://pastebin.com/Tds1KK8q
https://pastebin.com/cuTV0ANH
https://pastebin.com/tfvAj5zG
https://pastebin.com/YmTJJ9P4
https://pastebin.com/qr720c8a
https://pastebin.com/Qzx9z0jT
https://pastebin.com/phurPJTs
https://pastebin.com/gHHTkHD3
https://pastebin.com/ZwnPCDTj
https://pastebin.com/0DSjtHAt
https://pastebin.com/TftyJMxx
https://pastebin.com/vBBJvgLm
https://pastebin.com/NQYakApj
https://pastebin.com/meCbWSmb
https://pastebin.com/1VP7BDiy
https://pastebin.com/9dwnBKrH
https://pastebin.com/CfNxq9BD
https://pastebin.com/7SdTHhbB
https://pastebin.com/wjGatMb0
https://pastebin.com/Gv2kQv0K
https://pastebin.com/kqY5ZC2U
https://pastebin.com/NVQSYDC2
https://pastebin.com/p5cmmLTs
https://pastebin.com/ksUjWjEf
https://pastebin.com/iGqdf7JS
https://pastebin.com/1jPPW8SY
https://pastebin.com/zaiTAPGu
https://pastebin.com/ZyCEaVhf
https://pastebin.com/Z8nhRzGY
https://pastebin.com/4ANEqn0d
https://pastebin.com/B6aK9n6G
https://pastebin.com/0Tt4rGw1
https://pastebin.com/jV1WDDj1
https://pastebin.com/epsfQ9xR
https://pastebin.com/TDsuwVQq
https://pastebin.com/1sCfUZ1r
https://pastebin.com/rPZVVY9W
https://pastebin.com/YMAeatYs
https://pastebin.com/USZwiZLz
https://pastebin.com/0D4g5hpe
https://pastebin.com/aTgxSFAv
https://pastebin.com/d9yLduCs
https://pastebin.com/t2jGSt52
https://pastebin.com/ZCjZZ5cd
https://pastebin.com/fYBzqBQc
https://pastebin.com/rPSifbkZ
https://pastebin.com/r5hsmStB
https://pastebin.com/GBQJqUpA
https://pastebin.com/wi5TgU60
https://pastebin.com/17bmtrLm
https://pastebin.com/ymjx0ej6
https://pastebin.com/tnhPx5tx
https://pastebin.com/pKythJNa
https://pastebin.com/7rkBm8RR
https://pastebin.com/nKrgz0hx
https://pastebin.com/Ls4U30up
https://pastebin.com/GLrgZDKM
https://pastebin.com/QLenyQyb
https://pastebin.com/6kf7ctaD
https://pastebin.com/493xDX2E
https://pastebin.com/NNLBP1BA
https://pastebin.com/fQsBphT0
https://pastebin.com/iFUr5gqq
https://pastebin.com/YkZtGRGV
https://pastebin.com/CUgpDscE
https://pastebin.com/4BSRJRJ2
https://pastebin.com/rgaPPPXy
https://pastebin.com/e0MbXcLH
https://pastebin.com/0y661SPV
https://pastebin.com/WnhafHAL
https://pastebin.com/vdcuA6Wn
https://pastebin.com/0wP7iM13
https://pastebin.com/kSsbb7uN
https://pastebin.com/CKuHJUp0
https://pastebin.com/VFHuYY0w
https://pastebin.com/e57GMSyT
https://pastebin.com/JCJqt21y
https://pastebin.com/v03W6Wua
https://pastebin.com/Qh5cvmxD
https://pastebin.com/1n1rmrga
https://pastebin.com/EqpNrUd9
https://pastebin.com/hNzVc4VA
https://pastebin.com/wyd61PAX
https://pastebin.com/KUqAc0dA
https://pastebin.com/dAwvfQ3n
https://pastebin.com/tfhzjx0X
https://pastebin.com/qcry1nbc
https://pastebin.com/Xx1xKrr5
https://pastebin.com/z4RRXjbZ
https://pastebin.com/HFUs4bbZ
https://pastebin.com/7hFpzwBb
https://pastebin.com/kkxJEXkH
https://pastebin.com/8N244GbV
https://pastebin.com/6QgG0hC0
https://pastebin.com/SPfWYRk3
https://pastebin.com/CQQ6jn3J
https://pastebin.com/KHGYtVZu
https://pastebin.com/ibnJTTYm

收起阅读 »

nvue页面内单文件组件的国际化

先上场景案例:

nvue 页面中的弹窗组件 up-popup, 初始状态不显示 , 使用nvue国际化方案, 页面进入后, 不打开弹窗前就修语言, 回到该页面,打开弹窗, 则显示的是 修改前的语言

uni-i18n源代码原因:t 是惰性的

let t = (key, values) => {  
        if (typeof getApp !== 'function') {  
            // app view  
            /* eslint-disable no-func-assign */  
            t = function (key, values) {  
                return i18n.t(key, values);  
            };  
        }  
        else {  
            let isWatchedAppLocale = false;  
            t = function (key, values) {  
                const appVm = getApp().$vm;  

                if (appVm) {  
                    // 触发响应式  
                    appVm.$locale;  
                    if (!isWatchedAppLocale) {  
                        isWatchedAppLocale = true;  
                        watchAppLocale(appVm, i18n);  
                    }  
                }  
                return i18n.t(key, values);  
            };  
        }  
        return t(key, values);  
    };

我的解决方案:预热 t, 打破惰性

方案:

onMounted(() => {  
    t('appName');  
  });  

封装 hook

import { onMounted } from 'vue';  

/**  
 * @name useNvueI18nWarmup  
 * @description 一个旨在处理 nvue 页面中 i18n 响应式问题的组合式函数, 预热国际化 t,打破 t 的惰性。  
 */  
export function useNvueI18nWarmup(t: (key: string) => string) {  
  onMounted(() => {  
    t('appName');  
  });  
}

vue3组合式使用

import { useNvueI18nWarmup } from '@/common/useNvueI18nWarmup';  
import { initVueI18n } from '@dcloudio/uni-i18n'  

  // const messages = {} 此处内容省略,和 vue 全局引入的写法一致  

const { t } = initVueI18n(messages)  

useNvueI18nWarmup(t);  
继续阅读 »

先上场景案例:

nvue 页面中的弹窗组件 up-popup, 初始状态不显示 , 使用nvue国际化方案, 页面进入后, 不打开弹窗前就修语言, 回到该页面,打开弹窗, 则显示的是 修改前的语言

uni-i18n源代码原因:t 是惰性的

let t = (key, values) => {  
        if (typeof getApp !== 'function') {  
            // app view  
            /* eslint-disable no-func-assign */  
            t = function (key, values) {  
                return i18n.t(key, values);  
            };  
        }  
        else {  
            let isWatchedAppLocale = false;  
            t = function (key, values) {  
                const appVm = getApp().$vm;  

                if (appVm) {  
                    // 触发响应式  
                    appVm.$locale;  
                    if (!isWatchedAppLocale) {  
                        isWatchedAppLocale = true;  
                        watchAppLocale(appVm, i18n);  
                    }  
                }  
                return i18n.t(key, values);  
            };  
        }  
        return t(key, values);  
    };

我的解决方案:预热 t, 打破惰性

方案:

onMounted(() => {  
    t('appName');  
  });  

封装 hook

import { onMounted } from 'vue';  

/**  
 * @name useNvueI18nWarmup  
 * @description 一个旨在处理 nvue 页面中 i18n 响应式问题的组合式函数, 预热国际化 t,打破 t 的惰性。  
 */  
export function useNvueI18nWarmup(t: (key: string) => string) {  
  onMounted(() => {  
    t('appName');  
  });  
}

vue3组合式使用

import { useNvueI18nWarmup } from '@/common/useNvueI18nWarmup';  
import { initVueI18n } from '@dcloudio/uni-i18n'  

  // const messages = {} 此处内容省略,和 vue 全局引入的写法一致  

const { t } = initVueI18n(messages)  

useNvueI18nWarmup(t);  
收起阅读 »

nvue 踩坑整理,持续更新

iOS 安卓 nvue

最近使用nvue开发手机app,踩了不少坑,虽然大部分都勉强解决了,办法十分邪道,也仍有部分问题无解,特此整理公布,以赠后来者。

打包相关

1.关于midbutton高度对齐的问题。

答: 因原生平台差异,对图片型的midbutton设置高度,在安卓和苹果端可能产生无法垂直对齐的问题,又因为pages.json并未提供条件编译功能,建议手动校准修改两端高度,分别打包安卓和苹果端。

有解的问题

1.关于nvue的v-show能力。

答: 因 nvue 渲染层采用 weex,底层对元素的渲染机制,容易造成切换元素时的抖动现象。插件市场有相关 weex-v-show 插件,或者使用动态设置宽度和 opacity 达到类似效果。

2.关于input组件动态设置placeholder样式。

答: input组件可通过设置placeholder-style和placeholder-class属性设置placeholder的样式,但仅初次设置有效,无法动态改变,weex文档里提供了placeholder-color属性,可以通过css动态设置,改变placeholder的颜色;但是nvue编译器会对此属性抛出警告,在意者可通过写成行内样式进行规避。

3.关于overflow属性切换。

答: 因安卓端只支持overflow:hidden,此属性几乎无用,但weex文档里提到过只有同时满足以下四个条件,父view才会裁剪子view:
1.父view是 div, a, cell, refresh 或 loading
2.系统版本是 Android 4.3 或更高。
3.系统版本不是 Andorid 7.0。
4.父 view 没有 background-image 属性或系统版本是 Android 5.0 或更高。
这也意味着,似乎可以通过打破上述的任一条件限制,以此间接实现overflow:visible的效果?此想法仅笔者猜测,暂未实践。

4.关于subNvue的相关问题。

答: 可参考此回答,https://ask.dcloud.net.cn/article/41670?notification_id-1540939__item_id-65695。

5.关于swipera-action组件。

答: uni-ui提供的swipera-action在app端表现不佳,经常有异常抖动的问题,笔者在实践中采用了fui-swipe-action,当clickClose属性设置为false时,体验良好,基本满足需求。如果clickClose设置为true,也会出现和uni-ui类似的问题,猜测系bindingX的编写问题。

6.关于swiper组件。

答: 因底层限制,且nvue几乎不再维护,swiper相关的问题几乎无解,如设置动画时长或关闭动画等,笔者只能提出两种未经实践的想法,第一种是修改uniapp源码,手动添加weex的forbid-slide-animation属性;第二种即通过bindingX,自己实现一个swiper组件。

7.关于同行text设置不同样式的问题。

答: 因nvue不支持text嵌套,且仅有text组件能设置文字样式,此问题几乎无解,如果你没有文字换行的需求,可通过拆分文本,塞入不同的text并设置不同的样式;如果你需要换行,笔者的实践办法是更换为rich-text组件,数组型的nodes属性可以完美解决此类问题,虽然编写元素数组比较麻烦,但好在有效。

8.关于list和waterfall的长列表问题。

答: 正常使用几乎不存在性能问题,唯一的优化方向,可能就是当cell包含图片时,考虑优化图片大小减少内存消耗,另外cell组件的delete-animation="default"和insert-animation="default"需要明写,否则不会有默认的动画效果。

关于长列表内部的refresh和loading组件,refresh组件可以正常使用,几乎不存在兼容性问题,但loading组件需要分情况处理:

  1. 对于ios端,当列表元素长度大于1屏时,可以正常使用loading组件,当列表元素不足1屏时,会出现loading组件停留在列表顶部的Bug,建议在此种情况下,使用列表内置的header组件模拟loading(注意把header组件放置在列表底部),达到类似效果,当元素超过1屏后,再切换。
  2. 对于安卓端普通长列表,可以正常使用loading组件。
  3. 对于安卓端嵌套长列表,loading组件无法正常渲染,此种情况同样建议采用header组件模拟loading(注意把header组件放置在列表底部),基本可以达到比较良好的效果。

另外注意,无论是refresh还是Loading,其内置的loading-indicator组件都不建议使用,因其动画效果几乎不存在,考虑使用第三方图标替代。

9.底部输入区和键盘弹出的无缝动画协调问题。

答: 大部分App都会有留言或评论功能,往往需要从底部弹出自定义的留言区域,然而手机软键盘也会同时弹出,如何协同两者弹出时的动画效果,也需要分情况考虑,经观察,大部分app在ios端往往可以做到比较完美的效果,即软键盘从底部弹出时,留言区会顺势被顶起,整个过程流畅且无缝,观感十分好;而安卓端则没那么完美,软键盘和留言区的弹出动画往往是分先后完成的。

关于实际编码中如何解决这个问题,如果是原生编码,可以通过获取软键盘的属性来解决,uniapp做不到这点,只能曲线救国:

  1. 在ios端,考虑以subNvue来渲染弹出层,手动focus弹出层中的输入组件,并将adjust-position设置为true,弹出的键盘会把整个subNvue弹起,整个过程流畅无缝,效果很好,类似的办法也可以考虑使用透明的新页面,不过如何控制路由和如何隐藏都是个问题,subNvue会简单许多,另外第一次弹出的时候会稍有卡顿,这一点在其余app上也经常出现, 属于正常现象,后续的弹出就很正常了。
  2. 在安卓端,如上面所说的,暂时没有无缝弹出的办法,只能通过dom控制动画去尽量贴合键盘的弹起速度,关于这点不必花大力气去模拟贴合,毕竟做不到完美,能用即可。

10.关于安卓机进入新页面,image组件会出现闪白问题。

答: 因weex底层渲染机制,此问题无法直接解决,哪怕更换成Img组件一样会有此问题,只能通过两种方式缓解:

  1. 添加骨架图,等图片触发load事件后再显示。
  2. 在能计算出图片出现的位置和大小的情况下,可以考虑使用plus.nativeObj.view预先渲染图片,经笔者测试,此方式不会有延迟和闪白问题,结合startAnimation方法,几乎可以做到和页面进入动画无缝贴合,等到页面完全进入后,再关闭view即可。
  3. 关于第二点提到的方法,后经实践,预渲染的图片始终无法与页面进入动画同步,这里提出补救办法, 即将整个页面可见部分都预先渲染,等待页面进入动画结束后手动隐藏view,不过这种办法对于复杂页面来说似乎过于繁琐。

11.关于如何在nvue绘制三角形元素。

答: 传统web绘制三角形的办法基于border做文章,但nvue支持的css属性有限,无法直接解决,笔者的办法是渲染两个元素,将底层元素旋转,用上层元素遮盖底层元素,以此来达到模拟三角形的效果。

暂时无解的问题

1.image 组件 mode 设置为 aspectFit 的时候,如果给 Image 加上 border-radius,安卓端图片显示会出现异常拉伸,此问题系框架底层bug,目前无解决办法。

2.原生配置的tabbar,点击切换tabbar后如何触发动画特效问题,笔者有两者思路,一种是全局只有一个页面,即单页面富应用,nvue自行渲染底部tabbar,不过这种方案应该只适用小型项目;另一种则是结合gif图片,笔者看到网络上有提到,gif可设置只循环一次,具体效果暂未实践,留待后续验证。

继续阅读 »

最近使用nvue开发手机app,踩了不少坑,虽然大部分都勉强解决了,办法十分邪道,也仍有部分问题无解,特此整理公布,以赠后来者。

打包相关

1.关于midbutton高度对齐的问题。

答: 因原生平台差异,对图片型的midbutton设置高度,在安卓和苹果端可能产生无法垂直对齐的问题,又因为pages.json并未提供条件编译功能,建议手动校准修改两端高度,分别打包安卓和苹果端。

有解的问题

1.关于nvue的v-show能力。

答: 因 nvue 渲染层采用 weex,底层对元素的渲染机制,容易造成切换元素时的抖动现象。插件市场有相关 weex-v-show 插件,或者使用动态设置宽度和 opacity 达到类似效果。

2.关于input组件动态设置placeholder样式。

答: input组件可通过设置placeholder-style和placeholder-class属性设置placeholder的样式,但仅初次设置有效,无法动态改变,weex文档里提供了placeholder-color属性,可以通过css动态设置,改变placeholder的颜色;但是nvue编译器会对此属性抛出警告,在意者可通过写成行内样式进行规避。

3.关于overflow属性切换。

答: 因安卓端只支持overflow:hidden,此属性几乎无用,但weex文档里提到过只有同时满足以下四个条件,父view才会裁剪子view:
1.父view是 div, a, cell, refresh 或 loading
2.系统版本是 Android 4.3 或更高。
3.系统版本不是 Andorid 7.0。
4.父 view 没有 background-image 属性或系统版本是 Android 5.0 或更高。
这也意味着,似乎可以通过打破上述的任一条件限制,以此间接实现overflow:visible的效果?此想法仅笔者猜测,暂未实践。

4.关于subNvue的相关问题。

答: 可参考此回答,https://ask.dcloud.net.cn/article/41670?notification_id-1540939__item_id-65695。

5.关于swipera-action组件。

答: uni-ui提供的swipera-action在app端表现不佳,经常有异常抖动的问题,笔者在实践中采用了fui-swipe-action,当clickClose属性设置为false时,体验良好,基本满足需求。如果clickClose设置为true,也会出现和uni-ui类似的问题,猜测系bindingX的编写问题。

6.关于swiper组件。

答: 因底层限制,且nvue几乎不再维护,swiper相关的问题几乎无解,如设置动画时长或关闭动画等,笔者只能提出两种未经实践的想法,第一种是修改uniapp源码,手动添加weex的forbid-slide-animation属性;第二种即通过bindingX,自己实现一个swiper组件。

7.关于同行text设置不同样式的问题。

答: 因nvue不支持text嵌套,且仅有text组件能设置文字样式,此问题几乎无解,如果你没有文字换行的需求,可通过拆分文本,塞入不同的text并设置不同的样式;如果你需要换行,笔者的实践办法是更换为rich-text组件,数组型的nodes属性可以完美解决此类问题,虽然编写元素数组比较麻烦,但好在有效。

8.关于list和waterfall的长列表问题。

答: 正常使用几乎不存在性能问题,唯一的优化方向,可能就是当cell包含图片时,考虑优化图片大小减少内存消耗,另外cell组件的delete-animation="default"和insert-animation="default"需要明写,否则不会有默认的动画效果。

关于长列表内部的refresh和loading组件,refresh组件可以正常使用,几乎不存在兼容性问题,但loading组件需要分情况处理:

  1. 对于ios端,当列表元素长度大于1屏时,可以正常使用loading组件,当列表元素不足1屏时,会出现loading组件停留在列表顶部的Bug,建议在此种情况下,使用列表内置的header组件模拟loading(注意把header组件放置在列表底部),达到类似效果,当元素超过1屏后,再切换。
  2. 对于安卓端普通长列表,可以正常使用loading组件。
  3. 对于安卓端嵌套长列表,loading组件无法正常渲染,此种情况同样建议采用header组件模拟loading(注意把header组件放置在列表底部),基本可以达到比较良好的效果。

另外注意,无论是refresh还是Loading,其内置的loading-indicator组件都不建议使用,因其动画效果几乎不存在,考虑使用第三方图标替代。

9.底部输入区和键盘弹出的无缝动画协调问题。

答: 大部分App都会有留言或评论功能,往往需要从底部弹出自定义的留言区域,然而手机软键盘也会同时弹出,如何协同两者弹出时的动画效果,也需要分情况考虑,经观察,大部分app在ios端往往可以做到比较完美的效果,即软键盘从底部弹出时,留言区会顺势被顶起,整个过程流畅且无缝,观感十分好;而安卓端则没那么完美,软键盘和留言区的弹出动画往往是分先后完成的。

关于实际编码中如何解决这个问题,如果是原生编码,可以通过获取软键盘的属性来解决,uniapp做不到这点,只能曲线救国:

  1. 在ios端,考虑以subNvue来渲染弹出层,手动focus弹出层中的输入组件,并将adjust-position设置为true,弹出的键盘会把整个subNvue弹起,整个过程流畅无缝,效果很好,类似的办法也可以考虑使用透明的新页面,不过如何控制路由和如何隐藏都是个问题,subNvue会简单许多,另外第一次弹出的时候会稍有卡顿,这一点在其余app上也经常出现, 属于正常现象,后续的弹出就很正常了。
  2. 在安卓端,如上面所说的,暂时没有无缝弹出的办法,只能通过dom控制动画去尽量贴合键盘的弹起速度,关于这点不必花大力气去模拟贴合,毕竟做不到完美,能用即可。

10.关于安卓机进入新页面,image组件会出现闪白问题。

答: 因weex底层渲染机制,此问题无法直接解决,哪怕更换成Img组件一样会有此问题,只能通过两种方式缓解:

  1. 添加骨架图,等图片触发load事件后再显示。
  2. 在能计算出图片出现的位置和大小的情况下,可以考虑使用plus.nativeObj.view预先渲染图片,经笔者测试,此方式不会有延迟和闪白问题,结合startAnimation方法,几乎可以做到和页面进入动画无缝贴合,等到页面完全进入后,再关闭view即可。
  3. 关于第二点提到的方法,后经实践,预渲染的图片始终无法与页面进入动画同步,这里提出补救办法, 即将整个页面可见部分都预先渲染,等待页面进入动画结束后手动隐藏view,不过这种办法对于复杂页面来说似乎过于繁琐。

11.关于如何在nvue绘制三角形元素。

答: 传统web绘制三角形的办法基于border做文章,但nvue支持的css属性有限,无法直接解决,笔者的办法是渲染两个元素,将底层元素旋转,用上层元素遮盖底层元素,以此来达到模拟三角形的效果。

暂时无解的问题

1.image 组件 mode 设置为 aspectFit 的时候,如果给 Image 加上 border-radius,安卓端图片显示会出现异常拉伸,此问题系框架底层bug,目前无解决办法。

2.原生配置的tabbar,点击切换tabbar后如何触发动画特效问题,笔者有两者思路,一种是全局只有一个页面,即单页面富应用,nvue自行渲染底部tabbar,不过这种方案应该只适用小型项目;另一种则是结合gif图片,笔者看到网络上有提到,gif可设置只循环一次,具体效果暂未实践,留待后续验证。

收起阅读 »

解决 nvue 页面 input placeholder-style 无效的问题

nvue Android iOS input

背景:nvue 页面,设置 input 的 placeholder-style 属性不生效。

字体大小 使用 px(像素)

APP 端,属性使用小驼峰命名法:

<input placeholder="请输入" placeholder-style="fontSize: 12px; lineHeight: 12px; color: #666;" />

微信小程序端,属性保持原有写法:

<input placeholder="请输入" placeholder-style="font-size: 12px; line-height: 12px; color: #666;" />
继续阅读 »

背景:nvue 页面,设置 input 的 placeholder-style 属性不生效。

字体大小 使用 px(像素)

APP 端,属性使用小驼峰命名法:

<input placeholder="请输入" placeholder-style="fontSize: 12px; lineHeight: 12px; color: #666;" />

微信小程序端,属性保持原有写法:

<input placeholder="请输入" placeholder-style="font-size: 12px; line-height: 12px; color: #666;" />
收起阅读 »

解决 nvue 页面 uni-popup 不居中的问题

uni-popup

临时解决方案:

<uni-popup type="center">    
  <view class='wrapper'>    
    【这里放弹窗内容】    
  </view>    
</uni-popup>
.wrapper {    
  position: fixed;    
  top: 0;    
  right: 0;    
  bottom: 0;    
  left: 0;    
  z-index: 999999999;    

  justify-content: center;    
  align-items: center;    
}
继续阅读 »

临时解决方案:

<uni-popup type="center">    
  <view class='wrapper'>    
    【这里放弹窗内容】    
  </view>    
</uni-popup>
.wrapper {    
  position: fixed;    
  top: 0;    
  right: 0;    
  bottom: 0;    
  left: 0;    
  z-index: 999999999;    

  justify-content: center;    
  align-items: center;    
}
收起阅读 »

nvue中text标签无法自适应高度的问题

nvue

给父元素添加 justify-content: flex-start; 或者 align-items: flex-start;就可以了, 如何还是不行可以是用下面的方法

使用rich-text来解决, text标签是可以换行的, 但是没有办法撑开高度

// 数组
let arr = [
{
label: "账单编号",
value: [
{
name: "div",
attrs: {
style: "fontSize: 16px;color: #333333;line-height: 26px;",
},
text: _this.payInfo.checkInId,
},
],
},
]
// html
<view
class="similar-info-item"
v-for="(item, index) in _this.baseInfo"
key="index" >
<text class="label">{{ item.label }}:</text>
<view class="con">
<rich-text :nodes="item.value"></rich-text>
</view>
</view>
// css
.similar-info-item {
min-height: 26rpx;
display: flex;
flex-direction: row;
padding-left: 12px;
margin-bottom: 4px;
.label {
width: 90px;
font-size: 16px;
color: #333333;
line-height: 26px;
}
.con {
flex: 1;
line-height: 26rpx;
display: flex;
justify-content: center;
}
}
继续阅读 »

给父元素添加 justify-content: flex-start; 或者 align-items: flex-start;就可以了, 如何还是不行可以是用下面的方法

使用rich-text来解决, text标签是可以换行的, 但是没有办法撑开高度

// 数组
let arr = [
{
label: "账单编号",
value: [
{
name: "div",
attrs: {
style: "fontSize: 16px;color: #333333;line-height: 26px;",
},
text: _this.payInfo.checkInId,
},
],
},
]
// html
<view
class="similar-info-item"
v-for="(item, index) in _this.baseInfo"
key="index" >
<text class="label">{{ item.label }}:</text>
<view class="con">
<rich-text :nodes="item.value"></rich-text>
</view>
</view>
// css
.similar-info-item {
min-height: 26rpx;
display: flex;
flex-direction: row;
padding-left: 12px;
margin-bottom: 4px;
.label {
width: 90px;
font-size: 16px;
color: #333333;
line-height: 26px;
}
.con {
flex: 1;
line-height: 26rpx;
display: flex;
justify-content: center;
}
}
收起阅读 »

uni-list 中的的loadmore事件不执行

nvue 中 uni-list 的loadmore不执行。
研究了 一天,发现龟儿 ,
1、
uni-list 的组件中,有写事件代码,但是没有绑定到 list组件中
loadMore(e) {
this.$emit('scrolltolower');
},
既然这样,我就给他帮上。我就复制了 loadMore 粘贴到 组件上,于是
<list :bounce="false" :scrollable="true" show-scrollbar :render-reverse="renderReverse" @scroll="scroll" class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop"
loadmoreoffset="15" @loadMore="loadMore" >
坑来了,这样加上还是没有。

2、一遍一遍又看 了文档,开始觉得这球玩意是不是真的不支持。也许是因为这样,所以才没有添加的,但是list得文档说是支持的。
于是,一个字一个字看文档。最后发现 loadmore的这个m是小写的,于是就成功过了。真tm坑了我一天时间。

继续阅读 »

nvue 中 uni-list 的loadmore不执行。
研究了 一天,发现龟儿 ,
1、
uni-list 的组件中,有写事件代码,但是没有绑定到 list组件中
loadMore(e) {
this.$emit('scrolltolower');
},
既然这样,我就给他帮上。我就复制了 loadMore 粘贴到 组件上,于是
<list :bounce="false" :scrollable="true" show-scrollbar :render-reverse="renderReverse" @scroll="scroll" class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop"
loadmoreoffset="15" @loadMore="loadMore" >
坑来了,这样加上还是没有。

2、一遍一遍又看 了文档,开始觉得这球玩意是不是真的不支持。也许是因为这样,所以才没有添加的,但是list得文档说是支持的。
于是,一个字一个字看文档。最后发现 loadmore的这个m是小写的,于是就成功过了。真tm坑了我一天时间。

收起阅读 »

live-pusher开启补光灯

闪光灯 live_pusher

this.livePusher.toggleTorch()

this.livePusher.toggleTorch()

免费帮忙开发 安卓 ios 原生插件,刚学完练练手,有需要的留言(太难的就算了)

uniapp原生插件

免费开发安卓 ios 原生插件,刚学完练练手,有需要的留言(太难的就算了)

免费开发安卓 ios 原生插件,刚学完练练手,有需要的留言(太难的就算了)

nvue canvas踩坑经历

Webview canvas GCanvas nvue uniapp

总结就是:用到绘制海报类似的地方(drawImage)不要用nvue。

  1. nvue 不支持普通的canvas api绘制。
  2. 然后找到了gcanvas这个东西,在iOS上还行,可以用。
  3. 然后到Android上一测,拉垮了。报错:exception function:gcanvas setBackGround for android view, exception:WX_REND大概原因就是gcanvas在安卓上绘制图像很容易各种问题,gcanvas是个第三方团队产品,uniapp官方在社区也曾说不推荐使用gcanvas。确实支持度不好。
  4. 没办法,gcanvas不能用在安卓上,只能再看看其他办法。然后采用了nvue+webview的方案,nvue使用webview加载hybrid本地html,准备用web canvas来绘制,一通操作猛如虎,嗯,安卓上能正常了
  5. 然后到iOS上一看,又拉跨了,canvas.toDataURL时报错了:the operation is insecure。翻译一下就是资源跨域问题。还奇怪呢,本地html引用的都是相对路径的本地资源,咋还会跨域,一通查一通找,发现了官方给出的解释:wkwebview环境本地资源也算跨域。好吧,没办法,那继续看看有没有办法解决吧。后面在社区里看了下发现有两种办法 1.plus.io 相关api读取为本地路径。2.本地资源转成base64。一通操作一通调试后没走通,大概原因就是1.nvue webview环境下,不能用plus api。2.本地资源转base64,大概方法也是走的原生xhr或者plus转换,但是原生xhr还有canvas.toDataURL在这种情况都是有跨域问题,plus同原因1一样,也没有支持。
  6. 最后,不纠结了,掉头吧。然后页面转为vue文件渲染。然后就ok了。

nvue这个东西官方也不维护了,虽然确实性能比vue强,但真不适合用纯nvue来做项目,巨多坑。还是见仁见智吧,合适场景下vue+nvue结合使用。

nvue+vue画布绘制并导出图片的解决方案【插件】

继续阅读 »

总结就是:用到绘制海报类似的地方(drawImage)不要用nvue。

  1. nvue 不支持普通的canvas api绘制。
  2. 然后找到了gcanvas这个东西,在iOS上还行,可以用。
  3. 然后到Android上一测,拉垮了。报错:exception function:gcanvas setBackGround for android view, exception:WX_REND大概原因就是gcanvas在安卓上绘制图像很容易各种问题,gcanvas是个第三方团队产品,uniapp官方在社区也曾说不推荐使用gcanvas。确实支持度不好。
  4. 没办法,gcanvas不能用在安卓上,只能再看看其他办法。然后采用了nvue+webview的方案,nvue使用webview加载hybrid本地html,准备用web canvas来绘制,一通操作猛如虎,嗯,安卓上能正常了
  5. 然后到iOS上一看,又拉跨了,canvas.toDataURL时报错了:the operation is insecure。翻译一下就是资源跨域问题。还奇怪呢,本地html引用的都是相对路径的本地资源,咋还会跨域,一通查一通找,发现了官方给出的解释:wkwebview环境本地资源也算跨域。好吧,没办法,那继续看看有没有办法解决吧。后面在社区里看了下发现有两种办法 1.plus.io 相关api读取为本地路径。2.本地资源转成base64。一通操作一通调试后没走通,大概原因就是1.nvue webview环境下,不能用plus api。2.本地资源转base64,大概方法也是走的原生xhr或者plus转换,但是原生xhr还有canvas.toDataURL在这种情况都是有跨域问题,plus同原因1一样,也没有支持。
  6. 最后,不纠结了,掉头吧。然后页面转为vue文件渲染。然后就ok了。

nvue这个东西官方也不维护了,虽然确实性能比vue强,但真不适合用纯nvue来做项目,巨多坑。还是见仁见智吧,合适场景下vue+nvue结合使用。

nvue+vue画布绘制并导出图片的解决方案【插件】

收起阅读 »

无意中找到nvue国际化场景下每个nvue页面都需要引入VueI18n的解决方案

国际化 nvue
// nvue 目前的国际化方案需要在每个页面单独引入uni-i18n,后续框架会抹平差异,抹平差异后和 vue 页面一样只需要在 main.js 中引入  
<script>  
  import {  
    initVueI18n  
  } from '@dcloudio/uni-i18n'  

  // const messages = {} 此处内容省略,和 vue 全局引入的写法一致  

  const { t } = initVueI18n(messages)  

  export default {  
    data() {  
      return {  
      }  
    }  
  }  
</script>  

这是官网推荐的方案,需要在每个nvue都有这段代码

今天发现我们其实可以在app.vue的onLaunch中把t挂载到uni下,类似 uni.$locale = t去挂载,然后在nvue页面内直接使用 uni.$locale('common.edit')

下面是app.vue的示例代码

<script>  
    import {  
        initVueI18n  
    } from '@dcloudio/uni-i18n';  
    import messages from '@/locale';  
    const {  
        t  
    } = initVueI18n(messages);  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
            uni.$locale = t  
            uni.$language = uni.getLocale()  
        },  
        onShow: function() {  
            console.log('App Show')  
        },  
        onHide: function() {  
            console.log('App Hide')  
        },  
    }  
</script>  

<style lang="scss">  
</style>
继续阅读 »
// nvue 目前的国际化方案需要在每个页面单独引入uni-i18n,后续框架会抹平差异,抹平差异后和 vue 页面一样只需要在 main.js 中引入  
<script>  
  import {  
    initVueI18n  
  } from '@dcloudio/uni-i18n'  

  // const messages = {} 此处内容省略,和 vue 全局引入的写法一致  

  const { t } = initVueI18n(messages)  

  export default {  
    data() {  
      return {  
      }  
    }  
  }  
</script>  

这是官网推荐的方案,需要在每个nvue都有这段代码

今天发现我们其实可以在app.vue的onLaunch中把t挂载到uni下,类似 uni.$locale = t去挂载,然后在nvue页面内直接使用 uni.$locale('common.edit')

下面是app.vue的示例代码

<script>  
    import {  
        initVueI18n  
    } from '@dcloudio/uni-i18n';  
    import messages from '@/locale';  
    const {  
        t  
    } = initVueI18n(messages);  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
            uni.$locale = t  
            uni.$language = uni.getLocale()  
        },  
        onShow: function() {  
            console.log('App Show')  
        },  
        onHide: function() {  
            console.log('App Hide')  
        },  
    }  
</script>  

<style lang="scss">  
</style>
收起阅读 »

要用的来复制粘贴吧!对于 luanqing-popup-dialog 这个 nvue 气泡菜单组件的修改

要用的来复制粘贴吧!对于 luanqing-popup-dialog 这个 nvue 气泡菜单组件的修改

  • 因为插件市场上太乱了,很多组件都存在问题,这次用了一个 luanqing-popup-dialog 组件也是不能直接使用的,所以进行了魔改,现在能够直接使用了,自己用太无聊了,暂时不想发什么插件市场了,直接复制粘贴出来给大家用吧,以后有空了我再发插件市场

无论是 vue还是nvue 可以直接使用,严格遵循了 nvue 的写法,nvue写法是完全可以向下vue兼容的,所以直接放心用吧

效果图

截图url:https://upload-images.jianshu.io/upload_images/10916716-c850836fdd143e5a.png

  • 源码直接看附件吧,这里放在代码段里预览出来效果乱七八糟的

  • 效果截图图片不知为啥uniapp文章里显示不出来,直接点击链接自己看吧

  • popup-menu.vue

继续阅读 »

要用的来复制粘贴吧!对于 luanqing-popup-dialog 这个 nvue 气泡菜单组件的修改

  • 因为插件市场上太乱了,很多组件都存在问题,这次用了一个 luanqing-popup-dialog 组件也是不能直接使用的,所以进行了魔改,现在能够直接使用了,自己用太无聊了,暂时不想发什么插件市场了,直接复制粘贴出来给大家用吧,以后有空了我再发插件市场

无论是 vue还是nvue 可以直接使用,严格遵循了 nvue 的写法,nvue写法是完全可以向下vue兼容的,所以直接放心用吧

效果图

截图url:https://upload-images.jianshu.io/upload_images/10916716-c850836fdd143e5a.png

  • 源码直接看附件吧,这里放在代码段里预览出来效果乱七八糟的

  • 效果截图图片不知为啥uniapp文章里显示不出来,直接点击链接自己看吧

  • popup-menu.vue

收起阅读 »