小程序实用UI及常见问题解决

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

2018-12-25  本文已影响20人  Frankeen
效果图
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

上一篇下一篇

猜你喜欢

热点阅读