微信搜索superit|邀请体验:大数据, 数据管理、OLAP分析与可视化平台 | 赞助作者:赞助作者

gw

 

 

<template>

    <view class="music_player recording-audio">

        <image class="big-image" :src="picUrl" mode="aspectFill"></image>
        <view class="big-cover"></view>
        <view class="content">
            <nav-bar :statusHeight="statusHeight" :navBarHeight="navBarHeight" class="navswipper">
                <view class="tab">
                    <view :class="currentPage === 0 ? 'active': ''">音频</view>
                    <view class="divider">|</view>
                    <view :class="currentPage === 1 ? 'active': ''">字幕</view>
                </view>
            </nav-bar>
            <swiper :style="{height: contentHeight}" @change="handleSwiperChange">

            <swiper-item class="music">
                <!-- old -->
                <view class="album">
                    <image :src="picUrl" mode="widthFix"></image>
                </view>
                <view class="info">
                    <view class="title">{{songName}}</view>
                    <view class="subtitle">
                        <view class="singer">{{singer}}</view>
                        <view class="alias">专辑:{{albumName}}</view>
                    </view>
                </view>
                <view class="lyric">{{currentLyricInfo}}</view>

                <!-- new -->
                <view class="progress">
                    <!-- <view class="audio-number">{{format(current)}}</view> -->
                    <slider class="audio-slider" step="1" :activeColor="color" block-size="16" :value="current" :max="duration" @changing="[status.seeking = true,current=$event.detail.value]" @change="seek($event.detail.value)"></slider>
                    <!-- <view class="audio-number">{{format(duration)}}</view> -->
                    <view class="time">
                        <view class="current">{{format(current)}}</view>
                        <view class="duration">{{format(duration)}}</view>
                    </view>
                </view>
                <view class="operation">
                    <image class="btn btn-mode" :src="'/static/player/play_'+playMode[playModeIndex]+'.png'" @tap="handleModeChange"></image>
                    <image class="btn btn-prev" src="/static/player/play_prev.png" @click="handlePrev"></image>
                    <view class="audio-control audio-control-prev" @click="seek(current-5)" :style="{borderColor:color}">
                        <image src="./img/back.png"></image>
                    </view>
                    <view class="audio-control audio-control-switch" :class="{audioLoading:(status.playing && status.waiting)}" @click="!status.playing?play():pausePlay()">
                        <!-- <image :src="${(status.playing%20&&%20status.waiting)?'./img/more.png':(!status.playing?'./img/pause_icon.png':'./img/playvv_icon.png')}" ></image> -->
                        <image class="btn" src="./img/more.png" v-if="(status.playing && status.waiting)"></image>
                        <image class="btn" src="/static/player/play_resume.png" v-else-if="!status.playing"></image>
                        <image class="btn" src="/static/player/play_pause.png" v-else></image>
                    </view>
                    <view class="audio-control audio-control-next" @click="seek(current+5)">
                        <image class="btn" src="./img/back.png" style="transform: rotate(180deg);"></image>
                    </view>
                    <image class="btn btn-next" src="/static/player/play_next.png" @tap="handleNext"></image>
                    <image class="btn btn-music" src="/static/player/play_music.png" ></image>
                </view>

            </swiper-item>

            <swiper-item class="lyric">
                <!-- old -->
                <!-- <scroll-view :scroll-y="true" class="lyric-list" :scroll-top="lyricScrollTop" :scroll-with-animation="true">
                    <block v-for="(item,index) in lyricInfos" :key="index" :id="subtitle-${index}">
                        <view class="item" :class="currentLyricIndex === index ? 'active':''">{{item.text}}</view>
                    </block>
                </scroll-view> -->

                <!-- new -->
                <!-- <view class="text_box" v-if="showText"> -->
                    <!-- <input class="search_input" type="text" confirm-type="search" v-model="searchText"
                        placeholder="请输入要搜索的关键词" @confirm="search" placeholder-style="font-size:28rpx;color:#999999;" /> -->

                    <scroll-view :scroll-anchoring="true" :scroll-y="true" :scroll-into-view="textScrollTarget"
                        :style="{height:textPanelHeight+'px','min-height':'450rpx'}">
                        <view class="item" :id="'item'+index" @click="seek(item.startTime/1000)" :key="index"
                            v-for="(item, index) in textList"
                            :style="item.role === rightRoleName ? 'display:flex;justify-content: flex-end' : ''">
                            <view v-if="item.role === rightRoleName" style="display:flex;">
                                <view
                                    :class="[item.role !== rightRoleName ? 'leftSide' : 'rightSide', textIndex === index ? 'highlight' : '']">
                                    <view>
                                        <text>{{ item.text }}</text>
                                    </view>
                                </view>
                                <view class="avatar_right">{{item.role}}</view>
                            </view>
                            <view v-else style="display:flex;">
                                <view class="avatar_left">{{item.role}}</view>
                                <view :class="[item.role !== rightRoleName ? 'leftSide' : 'rightSide', textIndex === index ? 'highlight' : '']">
                                    <view>
                                        <text>{{ item.text }}</text>
                                    </view>
                                </view>
                            </view>
                        </view>
                    </scroll-view>

                    <!-- <view class="search_result" v-if="showFoundWords">
                        <view class="search_result_warpper">
                            <button @click="prev" class="iconfont">
                                &#xe601;
                            </button>
                            <view class="num">
                                {{ searchReulst.curRow + 1 }} / {{ searchReulst.rows.length }}
                            </view>
                            <button @click="next" class="iconfont" style="transform: rotate(180deg);">
                                &#xe601;
                            </button>
                        </view>
                    </view> -->

                <!-- </view> -->

            </swiper-item>
        </swiper>
        </view>
    </view>

</template>

<script>
    import { formatTime } from '@/util/format.js'
    export default {
        name: "gw-audiott",
        props: {
            src: String, //url
            startTime: { //播放起点
                type: Number,
                default: 0,
            },
            showText: { //是否显示对话
                type: Boolean,
                default: false
            },
            rightRole: { //显示在右边的对话的role 默认为第一条的role showText=true时生效
                type: String,
                require: false
            },
            list: { //文本集合 showText=true时生效
                type: Array,
                default () {
                    // 如下 属性名可以通过fields映射
                    // return [{
                    //  startTime: 111,
                    //  endTime: 222,
                    //  role: 'kefu',
                    //  text: '你好吗'
                    // },
                    // {
                    //  startTime: 333,
                    //  endTime: 444,
                    //  role: 'kehu',
                    //  text: '我很好'
                    // }]
                    return [];
                }
            },
            fields: { //属性映射 showText=true时生效
                type: Object,
                default () {
                    return {
                        startTime: 'startTime',
                        endTime: 'endTime',
                        role: 'role',
                        text: 'text'
                    }
                }
            },
            autoplay: { //是否自动播放
                type: Boolean,
                default: false
            }, //是否自动播放
            color: {
                type: String,
                default: '#169af3'
            }, //主色调
            debug: {
                type: Boolean,
                default: false
            },

        },
        computed: {
            setDuration() {
                return formatTime(this.duration)
            },
            setCurrentTime() {
                return formatTime(this.current)
            }
        },
        data() {
            return {
                isIOS: '', //手机系统
                audio: null,
                current: 0, //当前进度(s)
                duration: 0, //总时长(s)
                /**逻辑状态**/
                status: {
                    playing: false, //只有点击控制面板和播放结束可修改此状态
                    seeking: false, //是否处于拖动状态
                    waiting: false, //等待加载数据
                },

                textPanelHeight: 0, //对话框高度
                textScrollTarget: null, //滚动到当前说话位置
                textList: [], //对话信息
                rightRoleName: '', //显示在右边的人
                textIndex: -1, //当前高亮的文本

                searchText: '',
                showFoundWords: false,
                searchReulst: {
                    curRow: -1,
                    rows: []
                },

                currentPage: 0,

                picUrl: 'https://www.msnao.com/2019/03/835-200x100.jpg', //https://picsum.photos/200/100?random=101',
                songName: '',
                singer: '',
                albumName: '',

                statusHeight: 0,
                navBarHeight: 44,

                currentLyricInfo: '',
                playModeIndex: 0,
                playMode: ['order', 'random', 'repeat'],
            }
        },
        methods: {
            handleNext() {
                this.$emit('handleNext')
            },
            handlePrev() {
                this.$emit('handlePrev')
            },
            handleModeChange() {
                if (this.playModeIndex === 2) {
                    this.playModeIndex = 0
                } else {
                    this.playModeIndex++
                }
                this.$emit('handleModeChange', this.playModeIndex)
            },
            handleSwiperChange(event) {
                this.currentPage = event.detail.current
            },
            calcTextPosition() {
                if (this.status.seeking) {
                    return;
                }
                if (!this.showText) {
                    return;
                }
                if (!this.textList || this.textList.length <= 0) {
                    return;
                }
                const curTime = new Date().getTime();
                if ((this.duration - this.current > 1) &&
                    (this.lastCalcTime && curTime - this.lastCalcTime <= 300)) {
                    return;
                }
                this.lastCalcTime = curTime;
                const that = this;
                const _current = parseInt((this.current * 1000).toFixed(0))
                const _duration = parseInt((this.duration * 1000).toFixed(0))
                const _len = this.textList.length;
                let result = -1;
                let nearest = -1;
                if (this.textIndex > -1) {
                    for (let i = this.textIndex; i < _len; i++) {
                        const item = this.textList[i];
                        if (_current >= item.startTime && _current <= item.endTime) { //符合时间区
                            result = i;
                            break;
                        }
                        if (_current < item.startTime && _current < item.endTime) {
                            break;
                        }
                    }
                }
                if (result < 0) {
                    for (let i = 0; i < _len; i++) {
                        const item = this.textList[i];
                        if (_current >= item.startTime && _current <= item.endTime) { //符合时间区
                            result = i;
                            break;
                        }
                        if (_current <= item.startTime) {
                            nearest = (i > 0 ? i - 1 : i);
                            break;
                        }
                    }
                }
                if (result < 0 && nearest > -1) {
                    result = nearest;
                }
                if (result > -1 && result !== this.textIndex) { //不要改变这个代码的顺序
                    this.textScrollTarget = 'item' + result;
                }
                if (result > -1) {
                    this.textIndex = result;
                }

                this.assignItem(this.textList[result])
            },
            assignItem(item) {
              this.currentLyricInfo = item.text
              console.log(item)
            },
            seek(value) {
                if (value <= 0) {
                    value = 0
                }
                if (value >= this.duration) {
                    value = this.duration;
                }
                this.debug && console.log('调用Seek,当前audio状态=' + this.audio.paused + ',value=' + value);
                this.status.afterseek = true;
                this.status.seeking = true;
                this.status.playing = true;
                this.current = value;
                if (!this.audio.paused) { //暂停事件里调用this.audio.seek
                    this.audio.pause()
                } else { //已经是停止状态 必须先播放后再调用this.seek
                    this.status.afterseek = true;
                    if (!this.status.waiting) {
                        this.play();
                    }
                }
            },
            //点击播放按钮
            play() {
                console.log("1111111")
                this.debug && console.log('调用播放,当前audio状态=' + this.audio.paused);
                this.status.playing = true;
                if (this.audio.paused) {
                    if (this.isIOS) {
                        this.audio.play();
                    } else {
                        this.audio.play();
                        //兼容
                        // this.audio.autoplay = true;
                    }
                }
            },
            pausePlay() {
                console.log("22222222")
                this.debug && console.log('调用暂停,当前audio状态=' + this.audio.paused);
                this.status.playing = false;
                if (!this.audio.paused) {
                    if (this.isIOS) {
                        this.audio.pause();
                    } else {
                        this.audio.pause();
                        //兼容
                        //this.audio.autoplay = false;
                    }
                }
            },
            prev() {
                if (this.searchReulst.curRow <= 0) {
                    this.showFoundWords = false;
                    this.searchReulst = {
                        curRow: -1,
                        rows: []
                    }
                    return;
                }
                if (this.searchReulst.rows.length <= 0) {
                    this.showFoundWords = false;
                    this.searchReulst = {
                        curRow: -1,
                        rows: []
                    }
                    return;
                }
                const index = this.searchReulst.curRow - 1;
                this.searchReulst.curRow = index;
                this.seek(this.textList[this.searchReulst.rows[index].row].startTime / 1000);
            },
            next() {
                if (this.searchReulst.curRow >= this.searchReulst.rows.length) {
                    this.showFoundWords = false;
                    this.searchReulst = {
                        curRow: -1,
                        rows: []
                    }
                    return;
                }
                if (this.searchReulst.rows.length <= 0) {
                    this.showFoundWords = false;
                    this.searchReulst = {
                        curRow: -1,
                        rows: []
                    }
                    return;
                }
                const index = this.searchReulst.curRow + 1;
                if (index >= this.searchReulst.rows.length) {
                    this.showFoundWords = false;
                    this.searchReulst = {
                        curRow: -1,
                        rows: []
                    }
                    return;
                }
                this.searchReulst.curRow = index;
                this.seek(this.textList[this.searchReulst.rows[index].row].startTime / 1000);
            },
            search() {
                this.showFoundWords = false
                let result = [];
                this.textList.forEach(v => {
                    v.__FF__ = [{
                        value: [],
                        indexList: []
                    }];
                });
                if (!this.searchText) {
                    return (this.searchReulst = {
                        curRow: -1,
                        rows: []
                    });
                }
                this.textList.forEach((v, i) => {
                    if (v.text.includes(this.searchText)) {
                        let b = [...v.text.matchAll(new RegExp(this.searchText, 'ig'))];
                        b.forEach(v => {
                            let length = v[0].length;
                            let start = v.index;
                            v.index = [];
                            for (let i = start; i < start + length; i++) {
                                v.index.push(i);
                            }
                        });
                        let indexs = [];
                        b.map(v => v.index).forEach(v => {
                            v.forEach(o => indexs.push(o));
                        });
                        result.push({
                            row: i,
                            value: b,
                            indexList: indexs
                        });
                    }
                });
                if (result.length > 0) {
                    this.showFoundWords = true
                    this.searchReulst = {
                        curRow: -1,
                        rows: result
                    };
                    this.next()
                } else {
                    this.showFoundWords = false
                    this.searchReulst = {
                        curRow: -1,
                        rows: []
                    }
                }
                console.log(result, '搜索结果');
                this.textList.forEach((v, i) => {
                    result.forEach(o => {
                        if (i == o.row) {
                            v.__FF__ = o;
                        }
                    });
                });
            },
            mappedFields() {
                this.textScrollTarget = null; //滚动到当前说话位置
                this.textList = []; //对话信息
                this.textIndex = -1; //当前高亮的文本
                this.searchText = ''
                this.showFoundWords = false
                this.searchReulst = {
                    curRow: -1,
                    rows: []
                }
                this.rightRoleName = this.rightRole;
                if (!this.showText) {
                    return;
                }
                if (this.list.length <= 0) {
                    return;
                }
                if (this.rightRoleName == null) { //默认取第一条
                    this.rightRoleName = this.list[0][this.fields.role || 'role'] || '';
                }
                this.list.forEach(v => {
                    this.textList.push({
                        startTime: parseInt(v[this.fields.startTime || 'startTime'] || '0'),
                        endTime: parseInt(v[this.fields.endTime || 'endTime'] || '0'),
                        role: v[this.fields.role || 'role'] || '',
                        text: v[this.fields.text || 'text'] || ''
                    });
                });
            },
            init() {

                const res = uni.getSystemInfoSync()
                this.statusHeight = res.statusBarHeight
                this.contentHeight = (res.screenHeight - this.statusHeight - 90) + 'px'

                if (this.audio) {
                    this.audio.destroy();
                }
                this.resetStatus();
                this.audio = uni.createInnerAudioContext();
                //this.audio.obeyMuteSwitch = false;
                this.audio.startTime = this.startTime;
                this.audio.autoplay = this.autoplay;
                this.duration = 0;
                this.mappedFields();
                this.audio.src = this.src;
                if (this.autoplay) {
                    this.status.playing = true;
                }
                //音频进度更新事件
                this.audio.onTimeUpdate(() => {
                    console.log(this.duration, this.audio.currentTime)
                    if (!this.duration) {
                        this.duration = this.audio.duration
                    }
                    if (!this.status.seeking) {
                        this.current = this.audio.currentTime
                        this.calcTextPosition()
                        this.$emit('current', this.current);
                    }
                })
                //音频播放事件
                this.audio.onPlay(() => {
                    this.debug && console.log('开始播放,当前播放器状态=' + this.audio.paused);
                    this.status.waiting = false;
                    if (this.status.seeking && this.status.afterseek) {
                        this.status.afterseek = false;
                        this.seek(this.current);
                    } else {
                        this.status.seeking = false;
                        this.status.afterseek = false;
                    }
                })
                //音频暂停事件
                this.audio.onPause(() => {
                    this.debug && console.log('暂停播放,当前播放器状态=' + this.audio.paused);

                    //seek必须要在暂停播放后调用 否则没效果
                    if (this.status.seeking && this.status.playing) {
                        this.audio.seek(this.current);
                    } else {
                        this.status.seeking = false;
                        this.status.afterseek = false;
                    }
                })
                this.audio.onStop(() => {
                    this.debug && console.log('停止播放,当前播放器状态=' + this.audio.paused);
                })
                //音频等待
                this.audio.onWaiting(() => {
                    this.debug && console.log('等待音频数据,当前播放器状态=' + this.audio.paused)
                    this.status.waiting = true;
                    if (!this.audio.paused) {
                        this.audio.pause()
                    }
                })
                this.audio.onCanplay(() => {
                    this.debug && console.log('数据准备就绪,当前播放器状态=' + this.audio.paused)
                    this.status.waiting = false;
                    if (this.status.playing && !this.status.seeking && !this.status.afterseek) {
                        this.play()
                    }
                })

                //音频完成更改进度事件
                this.audio.onSeeked(() => {
                    if (this.status.seeking) { //这个事件触发可能不对 自己控制下
                        this.debug && console.log('Seeked,当前播放器状态=' + this.audio.paused)
                        this.status.seeking = false
                        if (this.status.playing && !this.status.waiting) {
                            this.play()
                        }
                    }
                })

                //音频结束事件
                this.audio.onEnded(() => {
                    this.debug && console.log('播放结束,当前播放器状态=' + this.audio.paused)
                    this.resetStatus();
                    this.$emit('end');
                })

                this.audio.onError((err) => {
                    this.debug && console.log('播放出错,当前播放器状态=' + this.audio.paused)
                    this.debug && console.error(err)
                    this.pausePlay();
                    this.resetStatus();
                    this.$emit('error', err);
                })

            },
            resetStatus() {
                this.status.playing = false;
                this.status.seeking = false;
                this.status.waiting = false;
                this.current = 0;
            },
            //格式化时长
            format(num) {
                return '0'.repeat(2 - String(Math.floor(num / 60)).length) + Math.floor(num / 60) + ':' + '0'.repeat(2 -
                    String(Math.floor(num % 60)).length) + Math.floor(num % 60)
            },
        },
        created() {
            const that = this;
            uni.getSystemInfo({
                success(res) {
                    that.isIOS = (res.system || '').startsWith('iOS')
                }
            });
            this.init();
        },
        beforeDestroy() {
            this.audio.destroy()
        },
        watch: {
            //监听切换,重新初始化
            src(src, old) {
                this.init();
            },
            list(list, old) {
                this.init();
            },
        },
        mounted() {
            //计算内容高度
            if (!this.showText) {
                return;
            }
            const that = this;
            this.$nextTick(function() {
                const query = uni.createSelectorQuery().in(that);
                query.select('.recording-audio').boundingClientRect()
                    .exec(res1 => {
                        if (res1) {
                            query.select('.navswipper').boundingClientRect()
                                .exec(res2 => {
                                    if (res2) {
                                        that.textPanelHeight = res2[0].height - res2[1].height 
                                        // query.select('.search_input')
                                        //  .boundingClientRect()
                                        //  .exec(res3 => {
                                        //      if (res3) {
                                        //          that.textPanelHeight = res3[0].height - res3[1].height - res3[2].height
                                        //      }
                                        //  })
                                    }
                                })
                        }
                    })
            })
        },
    }
</script>

<style lang="less" scoped>
    @font-face {
        font-family: 'icon';
        src: url('//at.alicdn.com/t/font_1104838_fxzimc34xw.eot');
        src: url('//at.alicdn.com/t/font_1104838_fxzimc34xw.eot?#iefix') format('embedded-opentype'),
            url('//at.alicdn.com/t/font_1104838_fxzimc34xw.woff2') format('woff2'),
            url('//at.alicdn.com/t/font_1104838_fxzimc34xw.woff') format('woff'),
            url('//at.alicdn.com/t/font_1104838_fxzimc34xw.ttf') format('truetype'),
            url('//at.alicdn.com/t/font_1104838_fxzimc34xw.svg#iconfont') format('svg');
    }

    .recording-audio {
        height: 100%;
        width: 100%;
    }
    .operation image{
        width: 60rpx;
        height: 60rpx;
    }
</style>
<style scoped>
    @import url("music_player.css");
</style>

<style lang="less">

    .item {
        overflow: hidden;
        display: flex;
        margin: 10upx 20upx;

        .leftSide,
        .rightSide {
            flex: 1;
        }

        .leftSide {
            float: left;
            padding: 20upx 20upx;
            background-color: #0299f9;
            color: #FFFFFF;
            border-radius: 10upx;
        }

        .rightSide {
            float: right;
            padding: 20upx 20upx;
            background-color: #f8f0d7;
            color: #666666;
            border-radius: 10upx;
        }

        .avatar_right {
            width: 85upx;
            height: 85upx;
            background-color: #fae4b8;
            color: #bf7900;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            margin-left: 20upx;
            overflow: hidden;
        }

        .avatar_left {
            width: 85upx;
            height: 85upx;
            background-color: #027cfe;
            color: #FFFFFF;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            margin-right: 20upx;
            overflow: hidden;
        }

        .highlight {
            color: red;
        }
    }

</style>

 

转载请注明:SuperIT » gw

喜欢 (0)or分享 (0)

您必须 登录 才能发表评论!