Open xccjk opened 2 years ago
给swiper设置catchtouchmove为true,设置swiper-item的catchtouchmove返回false
<swiper
class="swiper"
indicator-dots="{{false}}"
circular
autoplay="{{true}}"
vertical="{{true}}"
interval="{{3000}}"
duration="{{300}}"
easing-function="easeInOutCubic"
catchtouchmove="true"
>
<block wx:for="{{list}}" wx:key="index">
<swiper-item catchtouchmove="stopChange">
<view class="swiper-item">{{item.name}}</view>
</swiper-item>
</block>
</swiper>
stopChange() {
return false
}
背景:页面中有多条数据,每条数据都可以单独播放,不同时播放
核心逻辑就是每个音频单独初始化一个方法,每个音频播放的进度等信息都单独控制
通过wx.createInnerAudioContext
来控制
// wxml
<view wx:for="{{list}}" wx:key="id">
<view play="{{play}}" url="{{item.url}}" id="{{item.id}}">播放</view>
</view>
// js
Component({
data: {
play: false,
list: [
{ id: 1, url: '' },
{ id: 2, url: '' },
{ id: 3, url: '' },
],
id: null,
},
onLoad: function () {},
onShow() {},
onHide() {
this.audioPause();
},
onUnload() {
this.audioPause();
},
handleAudioPlay(e) {
const { play, url, id } = e.detail;
if (!this[`innerAudioContext${id}`]) {
this[`innerAudioContext${id}`] = wx.createInnerAudioContext();
}
if (play) {
this[`innerAudioContext${id}`].pause();
} else {
this[`innerAudioContext${id}`].autoplay = true;
this[`innerAudioContext${id}`].src = url;
this[`innerAudioContext${id}`].play();
}
this.setData({
play: !play,
id,
});
this.innerAudioContext.onEnded(() => {
this.setData({
play: false,
});
});
},
audioPause() {
const { id } = this.data;
this[`innerAudioContext${id}`].pause();
this.setData({
play: false,
});
},
});
当采用这个方法时,还有三种常见的场景需要考虑:
为什么需要考虑这三种情况呢,因为一般小程序退出当前页面或者切换到后台,需要停止当前播放的音乐
当通过返回键退出当前页面(或者点击页面跳转到其它页面),一般直接销毁音频初始化方法即可:
handlerBack() {
router.back()
const { id } = this.data
if (this[`innerAudioContext${id}`]) {
this[`innerAudioContext${id}`]. destroy()
}
}
当右上角退出小程序时
onUnload() {
const { id } = this.data
if (this[`innerAudioContext${id}`]) {
this[`innerAudioContext${id}`]. pause()
}
}
当按home键切换到后台时:
onHide() {
const { id } = this.data
if (this[`innerAudioContext${id}`]) {
this[`innerAudioContext${id}`]. pause()
}
}
当前音频是单独播放,只是针对每个音频初始化一个实例,假如需要同时播放多个音频,这种方式可能会导致内存不足导致的崩溃
提到音频播放控制,不得不提背景音频这个方法wx.getBackgroundAudioManager
很不幸,这个有坑。当使用场景为单音频播放或者不需要记录每个音频播放的位置时,使用它是一个合适的选择。当需要记录每个音频的播放位置时,这个就会有些问题了。问题来源backgroundAudioManager.seek
这个API。
重要的事情说3遍:backgroundAudioManager.seek在模拟器上正常,但是在真机上不生效,每次都会重新播 重要的事情说3遍:backgroundAudioManager.seek在模拟器上正常,但是在真机上不生效,每次都会重新播 重要的事情说3遍:backgroundAudioManager.seek在模拟器上正常,但是在真机上不生效,每次都会重新播
Component({
data: {
play: false,
list: [
{ id: 1, url: '' },
{ id: 2, url: '' },
{ id: 3, url: '' },
],
duration: {},
id: null,
},
onLoad() {},
onShow() {},
onHide() {},
onUnload() { },
play() {
wx.nextTick(() => {
const { duration = {} } = this.data;
const currentTime = duration[id] || 0;
backgroundAudioManager.title = '每日快讯';
backgroundAudioManager.epname = '';
backgroundAudioManager.singer = '';
backgroundAudioManager.coverImgUrl = '';
backgroundAudioManager.src = url;
if (currentTime) {
// 跳转到指定的进度
backgroundAudioManager.seek(currentTime);
backgroundAudioManager.play();
}
});
},
handleAudioPlay(e) {
const { play, url, id } = e.detail;
const backgroundAudioManager = wx.getBackgroundAudioManager();
if (play) {
backgroundAudioManager.pause();
// 记录当前音频播放的进度
wx.getBackgroundAudioPlayerState({
success(res) {
const { currentPosition } = res;
that.setData({
duration: {
...duration,
[id]: currentPosition,
},
});
},
});
// 点击的不是当前播放的音频
if (this.data.id !== id) {
this.play()
}
} else {
this.play()
}
this.setData({
play: !play,
id,
}),
},
});
问题产生原因:子组件properties中定义的字段类型不满足导致相关警告
比如定义组件中src属性为String类型时,当src传入null时,就会有上述警告出现
// imagex
Component({
options: {},
externalClasses: ['class'],
properties: {
src: String,
},
data: {
loaded: false,
},
lifetimes: {
attached() {},
},
methods: {},
});
使用
<imagex src="{{null}}" />
在平时的小程序开发过程中,可能会遇到下面这些小问题,虽然不影响开发过程,但是开发体验确会差一点,具体如下:
那么怎么避免重复的操作,特别是比较频繁的发布场景,可能每天需要多次的等待及上传操作。
对于问题1,相信很多人想的,就是每次输入命令时,微信开发者工具可以自动打开,并且可以打开当前指定的项目;对于问题2,每次输入构建发布命令时,先执行打包操作,然后自动执行上传操作,不需要人为等待打包结束已经人为点击发布按钮。
官方发布的cli命令,不需要开发者工具,进行小程序代码的上传、预览等操作等。网上这方面的文档挺多的,不细说。
采用这个方法的,需要提供小程序的秘钥与设置IP白名单,可能会存在一定的风险,比较适用于有独立的打包发布平台,在指定机器上进行打包操作
其实,通过nodejs写的脚本或者shell之类的,可以快速实现自动上传的效果。核心原理就是通过child_process开启一个多进程,在执行完打包命令后,运行官方提供的命令行V2,常见的API如下:
// 登录
cli login
// 预览
cli preview
// 上传
cli upload
// 启动开发者工具
cli open
相比miniprogram-ci,使用脚本的方式实现时,会依赖开发者工具,当微信开发者工具未登录时,运行自动上传命令会报错,因此也添加了判断未登录时,会在命令行中生成二维码,扫码登录即可。
同时,采用child_process不依赖任何三方包,每个人的电脑只要安装node环境与微信开发者工具就行了
具体实现如下:
// upload.js
#!/usr/bin/env
const child = require('child_process');
const exec = child.execSync;
function getDays() {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
const strDate = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
return `在${year}年${month}月${strDate}日${hours}点${minutes}分提交上传`;
}
function getName() {
const arr = process.cwd().split('/');
return arr[2];
}
const branch = child.execSync('git name-rev --name-only HEAD', { encoding: 'utf8' });
const config = {
path: `/Applications/wechatwebdevtools.app/Contents/MacOS/cli`,
projectPath: `${process.cwd()}/dist`,
version: `1.2.1`,
desc: `${getName()}${getDays()},发布分支${branch}`,
};
exec('npm run build', { stdio: 'inherit' });
child.exec(
`${config.path} upload --project ${config.projectPath} -v ${config.version} -d ${config.desc}`,
{ stdio: 'inherit' },
(err, res) => {
if (res) {
exec(`${config.path} login --qr-size small`, { stdio: 'inherit' });
exec(
`${config.path} upload --project ${config.projectPath} -v ${config.version} -d ${config.desc}`,
{ stdio: 'inherit' }
);
}
}
);
// package.json
"scripts": {
"upload": "node upload.js"
}
对于前端来说,平时写shell可能不是特别多,但是它真的可以解决非常多的问题,非常的便捷。
平时写shell,一般分两种,直接写shell,或者用三方包来写,比较知名的有shelljs与zx。对于shelljs,其实是对child_process做了一层封装,保证了多端语法一致性。而zx是Google出品的,语法更贴合前端。我们本次就是采用zx来实现。
// 全局安装zx
sudo npm install -g zx
具体实现如下:
// upload.mjs
#!/usr/bin/env zx
function getDays() {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
const strDate = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
return `在${year}年${month}月${strDate}日${hours}点${minutes}分 提交上传`;
}
const branch = await $`git branch --show-current`;
const config = {
path: `/Applications/wechatwebdevtools.app/Contents/MacOS/cli`,
projectPath: `${process.cwd()}/dist`,
version: `1.2.1`,
desc: `${getDays()},发布分支${branch}`,
};
await $`npm run build`;
await $`${config.path} upload --project ${config.projectPath} -v ${config.version} -d ${config.desc}`;
// package.json
"scripts": {
"upload": "zx upload.mjs"
}
// open.js
#!/usr/bin/env
const child = require('child_process');
const exec = child.execSync;
const path = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli';
const projectPath = `${process.cwd()}/dist`;
child.exec('git branch --show-current', (err, res) => {
console.log('当前分支:', res);
});
child.exec(`${path} open --project ${projectPath}`, (err, res) => {
if (res) {
exec(`${path} login --qr-size small`, { stdio: 'inherit' });
exec(`${path} open --project ${projectPath}`, { stdio: 'inherit' });
exec('npm run dev', { stdio: 'inherit' });
} else {
exec('npm run dev', { stdio: 'inherit' });
}
});
// package.json
"scripts": {
"open": "node open.js"
}
很多情况下在onShow生命周期进行接口请求,并且请求时设置了loading,在关闭预览时会出现loading动画
解决方案:
设置标识符,标记打开了预览弹窗
class App extends Component {
constructor(props) {
super(props);
this.onShow = false
}
async componentDidShow() {
if (this.onShow) {
this.onShow = false;
return;
}
await fn();
}
showModal = () => {
this.onShow = true;
wx.previewImage({});
};
...
}
export default App;
video标签背景色
想比很多人都想修改video的那个黑色背景吧,还是别做梦了,还不支持哈。
官方针对video背景色解读