美文网首页小程序实用UI及常见问题解决
小程序list分页加载组件和音频API使用

小程序list分页加载组件和音频API使用

作者: Frankeen | 来源:发表于2018-12-25 18:13 被阅读20次
    效果图
    2.gif
    场景

    使用小程序的时候经常会遇到一些分页加载展示的功能,都是list列表展示,只不过是里面的内容不同;然后每次有新的list都要把item遍历,然后又得加上底部的加载更多,加载完成等copy一份,操作十分繁琐;由此笔者进行了简单的封装,具体内容如下。

    编写一个common-list组件

    common-list.js:主要向外部提供两个属性list和loadMore;list用来存放需要展示的列表数组;loadMore用来控制页面加载更多的展示和关闭,通过给loadMore的enableLoadMore和haseMore来控制;然后向外部提供一个回调方法onClickListItem,通过该方法把item的所有点击事件回调到调用该common-list组件的组件或者页面,具体回调的值待会讲解item组件的时候再来分析;

    "use strict";
    Component({
        properties: {
            list: {
                type: Array,
                value: [],
                observer: "onListChange"
            },
            loadMore: {
                type: Object,
                value: {
                    enableLoadMore: false,
                    hasMore: true,
                },
                observer: "onLoadMoreChange"
            },
            
        },
    
    
        data: {
            enableLoadMore: false,
            hasMore: true,
        },
    
    
        methods: {
    
            onListChange: function onListChange() {
                this.setData({
                    list: this.data.list,
                });
            },
    
            /**
             * list里面item内部点击
             * 可通过e.detail.clickEvent.target知道是Item内部哪个子控件自身点击事件
             * @param {*} e 
             */
            onClickItem: function onClickItem(e) {
                // console.log(e);
                this.triggerEvent('onClickListItem', e.detail);
            },
            
            /**
             * 加载更多控件更新
             */
            onLoadMoreChange: function onLoadMoreChange() {
                this.setData({
                    enableLoadMore: this.data.loadMore.enableLoadMore,
                    hasMore: this.data.loadMore.hasMore,
                });
            },
    
        }
    });
    

    common-list.json:componentGenerics用来存在抽象节点的声明,比如我们这里声明了一个抽象节点list-item,list-item在common-list.wxml中代替外部传进来的item,有些类似slot;外部应用common-list的时候通过 generic:list-item="record-item" 给list-item赋值,record-item这里是自定义的item组件;
    该快理解模糊的话可以查看微信官方对抽象节点的描写文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/generics.html

    {
        "component": true,
        "componentGenerics": {
            "list-item": true
        }
    }
    //引用
     <common-list list="{{recordList}}"  loadMore="{{loadMore}}" generic:list-item="record-item" bindonClickListItem="onClickListItem" />
    

    common-list.wxml:主要就是遍历我们的item和添加加载更多样式,这里遍历的item就是我们的抽象节点list-item,然后把相应的值和索引传进去,再绑定一个队对外部的点击事件

    <template name="list-footer-view">
        <view wx:if="{{ enableLoadMore }}" class="loadmore_parent">
            <view class="loadmore" wx:if="{{ hasMore ? hasMore : false }}">
                <image class="loading" src="../../images/loading.svg"></image>
            </view>
            <view wx:else class="loadmore-end">
                <view class="loadmore-tip">
                    <view class="loadmore-end-line" />
                    <view class="loadmore-end-tip">
                        {{(listNoMoreDataTip ? listNoMoreDataTip : '没有更多内容了')}}
                    </view>
                    <view class="loadmore-end-line" />
                </view>
            </view>
        </view>
    </template>
    <view class="container">
        <block wx:if="{{list !== null && list.length !== 0}}">
            <block wx:for="{{list}}" wx:for-item="item" wx:key="{{index}}">
                <list-item item="{{item}}" index="{{index}}" bindonClickItem="onClickItem" />
            </block>
            <template is="list-footer-view" data="{{enableLoadMore:enableLoadMore,hasMore:hasMore}}" />
        </block>
    </view>
    
    编写一个record-item组件

    该组件就是我们的list里面的item;这里我这个组件内容可能相对复杂,因为包含了音频的使用;大家使用的时候只需要根据自己需要抽取就好;该组件提供了两个属性item和index,item主要用来存放item需要展示的数据,index主要用来几张item在list的索引;对外部提供一个onClickItem回调方法,该方法会回调以下基本数据clickEvent里面的target标识该点击事件是item哪个控件的点击事件,index代表item的索引。

    {
    clickEvent:{target: "play"},
    index:0
    }
    

    其他数据大家根据自己需要拓展
    record-item.js

    'use strict';
    
    Component({
        properties: {
            item: {
                type: Object,
                // value: {
                //     isPlay:false,
                //     name:'王先生',
                //     recordSrc:'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E06DCBDC9AB7C49FD713D632D313AC4858BACB8DDD29067D3C601481D36E62053BF8DFEAF74C0A5CCFADD6471160CAF3E6A&fromtag=46',
                //     phone:'13168849257',
                //     type:'安居客来电',
                //     rPersonnel:'万兴',
                //     duration:'30:22',
                //     createTime:'2019-11-11 22:22:12',
                //     curTime:'30:22',
                // }
                observer: 'onDataChange'
            },
            index: {
                type: Number,
                value: "",
            }
        },
    
        data: {
            curTime: '00:00',
            sliderValue: '',
            pause:false,
        },
    
        methods: {
            onDataChange: function onDataChange() {
                this.setData({
                    item: this.data.item,
                });
            },
            /**
             * 点击播放
             * @param {*} e 
             */
            onClickPlay: function onClickPlay(e) {
                let data = e.currentTarget.dataset.item;
                if (data.isPlay) {
                    getApp().getAudioContext().pause();
                    this.setData({
                        pause: true,
                    })
                }else{
                    let _this = this;
                    getApp().getAudioContext().src = data.recordSrc;
                    getApp().getAudioContext().loop = true;
                    getApp().getAudioContext().onPlay(function () {});
                    getApp().getAudioContext().onTimeUpdate(function () {
                        _this.setData({
                            sliderValue: (getApp().getAudioContext().currentTime / getApp().getAudioContext().duration) * 100,
                            curTime: _this.formatTime(getApp().getAudioContext().currentTime),
                        })
                    })
                    if(!_this.data.pause){
                        getApp().getAudioContext().stop();
                    }
                    _this.setData({
                        pause: false,
                    })
                    getApp().getAudioContext().play()
                }
                data.index = this.data.index;
                let clickEvent = {};
                clickEvent.target = 'play';
                data.clickEvent = clickEvent;
                this.triggerEvent('onClickItem', data);
            },
    
            /**
             * 后退
             * @param {*} e 
             */
            onClickPre: function onClickPre(e) {
                let _this = this;
                let currentTime = getApp().getAudioContext().currentTime - 10;
                if (currentTime < 0) {
                    currentTime = 0;
                }
                _this.setData({
                    sliderValue: (currentTime / getApp().getAudioContext().duration) * 100,
                    curTime: _this.formatTime(currentTime)
                })
                getApp().getAudioContext().seek(currentTime);//通过滑块控制音频进度
            },
    
    
            /**
             * 前进
             * @param {*} e 
             */
            onClickNext: function onClickNext(e) {
                let _this = this;
                let currentTime = getApp().getAudioContext().currentTime + 10;
                let duration = getApp().getAudioContext().duration;
                if (currentTime > duration) {
                    currentTime = duration;
                }
                _this.setData({
                    sliderValue: (currentTime / duration) * 100,
                    curTime: _this.formatTime(currentTime)
                })
                getApp().getAudioContext().seek(currentTime);//通过滑块控制音频进度
            },
    
    
            /**
             * 监听slider
             */
            onRecordSliderListener: function onRecordSliderListener(e) {
                let _this = this;
                var per = e.detail.value / 100;
                var long = per * getApp().getAudioContext().duration;
                this.setData({
                    curTime: _this.formatTime(long)
                })
                getApp().getAudioContext().seek(long);//通过滑块控制音频进度
            },
    
    
            /**
             * 时间秒数格式化
             * @param s 时间戳(单位:秒)
             * @returns {*} 格式化后的时分秒
             */
            formatTime: function formatTime(s) {
                var t = '';
                s = parseInt(s);
                if (s > -1) {
                    var hour = Math.floor(s / 3600);
                    var min = Math.floor(s / 60) % 60;
                    var sec = s % 60;
                    if (hour < 10) {
                        // t = '0' + hour + ":";
                    } else {
                        t = hour + ":";
                    }
    
                    if (min < 10) { t += "0"; }
                    t += min + ":";
                    if (sec < 10) { t += "0"; }
                    t += sec.toFixed(0);
                }
                return t;
            }
        }
    });
    

    record-item.wxml:跟我们平时定义的item差不多,唯一有差异就是,需要回调到外部的点击事件需要绑定onClickItem方法

    <view class="container">
        <view class="content_container">
            <view class="content">
                <view class="title">{{item.phone+'('+item.name+')'}}</view>
                <view class="des">{{item.type + ' | 接电员 : ' + item.rPersonnel + ' | 通话时间:' + item.duration}} </view>
                <view class="time">{{item.creatTime}}</view>
            </view>
            <image class="play_icon" data-item="{{item}}" src="{{item.isPlay?'/images/ic_record_pause.png':'/images/ic_record_play.png'}}" bindtap="onClickPlay" />
        </view>
        <view class="play_container" wx:if="{{item.isPlay}}">
            <view class="cur_time">{{curTime}}</view>
            <slider class="record_slider" bindchange="onRecordSliderListener" value="{{sliderValue}}" block-size="12" block-color="#00000000"  activeColor="#DDDDDDFF" backgroundColor="#EEEEEEFF"/>
            <image class="pre" src="{{'/images/ic_record_pre.png'}}"  bindtap="onClickPre" />
            <image class="next" src="{{'/images/ic_record_next.png'}}"  bindtap="onClickNext"  />
        </view>
        <view class="line"></view>
    </view>
    
    音频API使用

    微信官方文档:https://developers.weixin.qq.com/miniprogram/dev/api/InnerAudioContext.html
    需要注意的是使用onTimeUpdate监听进度之前,要设置onPlay播放监听,不然可能监听不到进度。
    由于这里我的音频播放是整个列表任何一个item都可以播放,所以我在app.js里面声明了一个音频上下文,然后在item内部直接调用该音频上下文;

    
        /**
         * 音频播放上下文
         */
        audioContext: null, 
    
        /**
         * 获取当前音频上下文
         */
        getAudioContext: function getAudioContext() {
            if(this.audioContext==null){
                this.audioContext = wx.createInnerAudioContext();
            }
            return this.audioContext;
        },
    
        /**
         * 摧毁当前音频上下文
         */
        destroyAudioContext: function destroyAudioContext() {
            if(this.audioContext!=null){
                this.audioContext.stop();
                this.audioContext.destroy
            }
        },
    
    

    整个列表音频播放,暂停,快进,后退流程思路如下:
    1,首先item外部参数要含有一个播放和暂停参数isPlay,用来标识item当前是播放还是停止状态;
    2,当点击播放的时候,通过 getApp().getAudioContext()获得音频上下文;为音频上下文设置src播放地址,设置loop为循环播放,设置onPlay和onTimeUpdate回调;然后判断该音频是否点击过暂停,如果点击过需要先调用stop方法,为了避免列表存在含有相同src的时候,点击播放音频不能从头开始播放;然后再调用播放方法play;最后通过回调onClickItem方法,更新列表样式;
    3,当点击暂停的时候,直接调用pause方法就好;
    4,当点击快进和后退的时候使用seek方法就好;

    详细使用可以看record-item.js

    代码详细地址:https://github.com/fuxingkai/frankui-weapp

    相关文章

      网友评论

        本文标题:小程序list分页加载组件和音频API使用

        本文链接:https://www.haomeiwen.com/subject/nsoflqtx.html