123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- <template>
- <view class="lyric-main" :style="{
- width:cuAreaStyle.width,
- height:cuAreaStyle.height,
- backgroundImage:cuAreaStyle.background}">
- <scroll-view id="lyric-show" class="lyric-show" scroll-with-animation="true" :scroll-y="true" :scroll-into-view="showLyricId" @scroll="lyricScroll"
- :style="{
- top:scrollView.top,
- width:cuAreaStyle.width,
- height:scrollView.height}">
- <view class="lrc-item" v-for="(v, i) in mLyrics.lrcs" :key="i" @tap="select(v,i)" @longpress="itemLongpress(v,i)"
- @touchstart="textTouchstart" @touchcancel="textTouchEnd" @touchend="textTouchEnd">
- <text class="check" v-if="selectStatus && selects[i] && v" value="[v,i]">✔</text>
- <text class="lrc" :id="'lrc-' + i"
- :style="{
- opacity:touchIndex == i && touchStatus && !selectStatus ? 0.8 : 1,
- color:((curLyricIndex == i || (touchIndex ==i && touchStatus)) && !selectStatus) ? cuLyricStyle.activeColor : cuLyricStyle.color,
- fontSize:((curLyricIndex == i || (touchIndex ==i && touchStatus))&& !selectStatus) ? cuLyricStyle.activeFontSize : cuLyricStyle.fontSize,
- height:((curLyricIndex == i || (touchIndex ==i && touchStatus))&& !selectStatus) ? cuLyricStyle.activeLineHeight : cuLyricStyle.lineHeight,
- backgroundColor:(selectStatus && selects[i] && v) ? cuLyricStyle.selectBGColor:'inherit'
- }">{{v}}</text>
- </view>
- </scroll-view>
- <view class="center-view" :style="{top:centerLineTop,display:showCenterView?'flex':'none',fontSize:cuCenterStyle.fontSize,height:cuCenterStyle.height}">
- <image v-if="cuCenterStyle.btnImg" class="center-btn" @click="centerBtnClick" :src="cuCenterStyle.btnImg" :style="{width:cuCenterStyle.height,height:cuCenterStyle.height}"></image>
- <view v-else class="center-btn" @click="centerBtnClick">{{cuCenterStyle.btnText}}</view>
- <view class="center-line" :style="{height:cuCenterStyle.lineHeight,backgroundColor:cuCenterStyle.color}"></view>
- <view class="center-time">{{centerTime}}</view>
- </view>
- <view class="selectControl" v-if="selectStatus" :style="{color:cuSelectControlStyle.color,backgroundColor:cuSelectControlStyle.backgroundColor,fontSize:cuSelectControlStyle.itemFontSize}">
- <view class="selectAll" @click="selectAll" :style="{borderRadius:cuSelectControlStyle.itemBorderRadius,backgroundColor:cuSelectControlStyle.itemBackgroundColor}">{{selectAllStatus ? '全不选' : '全选'}}</view>
- <view class="copy" @click="copyLyric" :style="{borderRadius:cuSelectControlStyle.itemBorderRadius,backgroundColor:cuSelectControlStyle.itemBackgroundColor}">复制歌词</view>
- <view class="cancel" @click="selectCancel" :style="{borderRadius:cuSelectControlStyle.itemBorderRadius,backgroundColor:cuSelectControlStyle.itemBackgroundColor}">取消</view>
- </view>
-
- </view>
- </template>
- <script>
- export default{
- data() {
- return {
- font2line4height: 2.5,
- selectAllStatus: false,
- selects: {}, // 选择状态下选中的歌词id
- scrollStatus: false, // 用于判断是否是在拖动歌词,是否应该开启选择歌词模式
- touchIndex: 0,
- curLyricId: 'lrc-0', // 时间进度控制
- showLyricId: 'lrc-0', // 界面显示,时间和touch控制
- curLyricIndex: 0,
- selectStatus: false,
- touchStatus: false,
- showCenterView: false,
- spaceLineNum: 0,
- timeout: {
- centerViewHide: null,
- goCenter: null,
- },
- cuSelectControlStyle: {
- height: '40px',
- backgroundColor: 'grey',
- itemFontSize: '16px',
- itemBorderRadius: '99px',
- itemBackgroundColor: '#00ffff'
- },
- cuLyricStyle: {
- color: "#000000",
- activeColor: '#ffffff',
- fontSize: '16px',
- activeFontSize: '16px',
- lineHeight: '40px',
- activeLineHeight: '40px',
- selectedBGColor: 'inherit'
- },
- cuAreaStyle: {
- width: '100vw',
- height: '70vh',
- background: 'linear-gradient(#8cc8b4, #ffaa7f, #8cc8b4)',
- },
- cuCenterStyle: {
- btnImg: '',
- btnText: 'btn',
- color: '#ffffff',
- lineHeight: '1px',
- height: '16px',
- fontSize: '14px',
- },
- scrollView: {
- height: '75%',
- top: 0
- },
- screen: {
- width:0,
- height:0,
- px2rpxscale: 1,
- },
- }
- },
- beforeMount() {
- const res = uni.getSystemInfoSync()
- this.screen.width = res.windowWidth
- this.screen.height = res.windowHeight
- this.screen.px2rpxscale = 750 / res.windowWidth;
- this.cuAreaStyle = Object.assign(this.cuAreaStyle, this.areaStyle)
- this.cuLyricStyle = Object.assign(this.cuLyricStyle, this.lyricStyle)
- this.cuCenterStyle = Object.assign(this.cuCenterStyle, this.centerStyle)
- this.cuSelectControlStyle = Object.assign(this.cuSelectControlStyle, this.selectControlStyle)
- if (this.lyricStyle){
- if(!('activeFontSize' in this.lyricStyle)){
- this.cuLyricStyle['activeFontSize'] = this.cuLyricStyle['fontSize']
- }
- if(!('lineHeight' in this.lyricStyle)){
- let s = this.sizeDeal(this.cuLyricStyle['fontSize'])
- this.cuLyricStyle['lineHeight'] = s[0] * this.font2line4height + s[1] // 默认行高为字体大小的倍数
- }
- if(!('activeLineHeight' in this.lyricStyle)){
- let s = this.sizeDeal(this.cuLyricStyle['activeFontSize'])
- this.cuLyricStyle['activeLineHeight'] = s[0] * this.font2line4height + s[1]
- }
- }
- if (this.centerStyle){
- if (!('height' in this.centerStyle)){
- let s = this.sizeDeal(this.cuCenterStyle['fontSize'])
- this.cuCenterStyle['height'] = s[0] * 1.2 + s[1]
- }
- }
-
- },
- mounted() {
- this.$nextTick(() => {
- const query = uni.createSelectorQuery().in(this)
- query.select(".lyric-main").boundingClientRect(res =>{
- this.cuAreaStyle.height = res.height + 'px'
- let size = this.sizeDeal(this.cuLyricStyle.lineHeight)
- let asize = this.sizeDeal(this.cuLyricStyle.activeLineHeight)
- let sumLine = Math.floor((res.height - asize[0]) / size[0]) // 不包含activeLine
- if (sumLine % 2 !== 0){
- sumLine -= 1
- }
- this.spaceLineNum = Math.floor(sumLine / 2)
- //this.scrollView.height = sumLine * size[0] + asize[0] + 'px'
- this.scrollView.top = (res.height - (sumLine * size[0] + asize[0])) / 2 + 'px'
- }).exec()
- })
- },
- props: {
- selectControlStyle: {
- default: () => {},
- type: Object
- },
- centerStyle:{
- default: () => {},
- type: Object
- },
- lyrics: {
- default: () => [],
- type: Array
- },
- curTime: {
- default: 0,
- type: [Number, String]
- },
- lyricStyle: {
- default: () => {},
- type: Object
- },
- areaStyle: {
- default: () => {},
- type: Object
- }
- },
- watch: {
- curTime(t) {
- let index = this.getIndex(t, this.mLyrics.times)
- this.curLyricIndex = index + this.spaceLineNum
- this.curLyricId = 'lrc-' + index
- if (!this.touchStatus && !this.selectStatus){
- this.showLyricId = this.curLyricId
- }
- },
- },
- computed: {
- lineHeightNum(){
- let size = this.sizeDeal(this.cuLyricStyle.fontSize)
- return size[0] * this.font2line4height
- },
- centerTime(){
- let index = this.touchIndex - this.spaceLineNum
- let s = this.mLyrics.times[index]
- return this.timeFormat(s)
- },
- centerLineTop(){
- let h = this.sizeDeal(this.cuAreaStyle.height)
- h = h[0] / 2 - this.sizeDeal(this.cuCenterStyle.height)[0] / 2 + h[1]
- return h
- },
- lineHeight() {
- let size = this.sizeDeal(this.cuLyricStyle.fontSize)
- return size[0] * 2 + size[1]
- },
- activeLineHeight() {
- let size = this.sizeDeal(this.cuLyricStyle.activeFontSize)
- return size[0] * 2 + size[1]
- },
- mLyrics () {
- return this.lrcDeal(this.lyrics)
- }
- },
- methods: {
- selectAll(){
- this.selectAllStatus = !this.selectAllStatus
- this.$nextTick(() => {
- if (this.selectAllStatus){
- for (let i=0;i<this.mLyrics.lrcs.length;i++){
- let lrc = this.mLyrics.lrcs[i]
- if(lrc){
- this.selects[i] =lrc
- }
- }
- }
- else{
- this.selects = {}
- }
- this.$forceUpdate()
- })
- },
- copyLyric(){
- let datas = {}
- for(let k in this.selects){
- datas[this.mLyrics.times[k - this.spaceLineNum]] = this.selects[k]
- }
- this.$emit('copyLyrics',{lyrics:datas})
- this.selectCancel()
- },
- selectCancel(){
- this.selectStatus = false
- this.touchStatus = false
- this.selectAllStatus = false
- this.selects = {}
- },
- itemLongpress(v,i){
- if (!this.scrollStatus && !this.selectStatus){
- this.selects = {}
- this.selectModel()
- if (v){
- if(this.selects[i]){
- delete this.selects[i]
- }
- else{
- this.selects[i] = v
- }
- }
- this.$forceUpdate()
- }
- },
- select(v,i){
- if(this.selects[i]){
- delete this.selects[i]
- }
- else{
- this.selects[i] = v
- }
- this.$forceUpdate()
- },
- selectModel() {
- this.showCenterView = false
- this.touchStatus = false
- this.selectStatus = true
- },
- centerBtnClick() {
- let lrc = this.mLyrics.lrcs[this.touchIndex]
- let ctime = Number(this.mLyrics.times[this.touchIndex - this.spaceLineNum])
- this.$emit('centerBtnClick',{centerTime:ctime,centerLyric:lrc})
- },
- textTouchstart() {
- clearTimeout(this.timeout.goCenter)
- clearTimeout(this.timeout.centerViewHide)
- this.touchStatus = true
- },
- textTouchEnd() {
- this.touchStatus = 2
- this.scrollStatus = false
- if (!this.selectStatus){
- this.timeout.goCenter = setTimeout(()=>{
- this.showLyricId = 'lrc-' + (this.touchIndex - this.spaceLineNum)
- },500)
- this.timeout.centerViewHide = setTimeout(this.clearTouch,5000)
- }
- },
- clearTouch() {
- this.touchStatus = false
- this.showLyricId = this.curLyricId
- this.showCenterView = false
- },
- lyricScroll(e) {
- let top = e.detail.scrollTop
- let topIndex = Math.round(top / this.lineHeightNum)
- this.touchIndex = topIndex + this.spaceLineNum
- if (this.touchStatus === true && !this.selectStatus){
- this.scrollStatus = true
- }
- if (this.touchStatus && !this.selectStatus && !this.showCenterView){
- this.showCenterView = true
- }
- this.$forceUpdate()
- },
- getIndex(t, items) {
- t = Number(t)
- let index = 0
- for ( var k_ in items){
- let k = Number(items[k_])
- if (t == k){
- return index
- }
- else if(t < k){
- return index - 1
- }
- else if ( index == items.length - 1){
- return items.length - 1
- }
- index += 1
- }
- },
- timeFormat(t){
- t = Number(t)
- if(t >= 0){
- let h = parseInt(t / 3600)
- let m = parseInt(t / 60) - h*60
- let s = parseInt(t) - m*60
- if (h.toString().length == 1){h = '0' + h}
- if (m.toString().length == 1){m = '0' + m}
- if (s.toString().length == 1){s = '0' + s}
- if (h != '00'){
- return h + ":" + m + ":" + s
- }
- else {
- return m + ":" + s
- }
- }
- else {
- return ''
- }
- },
- sizeDeal(size) {
- // 分离字体大小和单位,rpx 转 px
- let s = Number.isNaN(parseFloat(size)) ? 0 : parseFloat(size)
- let u = size.toString().replace(/[0-9\.]/g, '')
- if (u == 'rpx') {
- s /= this.screen.px2rpxscale
- u = 'px'
- } else if (u == '') {
- u = 'px'
- }else if (u == 'vw') {
- u = 'px'
- s = s / 100 * 750 / this.screen.px2rpxscale
- }
- return [s, u, s + u]
- },
- lrcDeal(lrcl) {
- let lrcj = {lrcs:[],times:[]}
- if (lrcl.length < 1){
- //没有歌词
- lrcj.lrcs.push('')
- }
- else{
- for (let i = 0; i < lrcl.length; i++ ){
- let lrc = lrcl[i].toString()
- let tl = lrc.split(']')
- if (tl.length > 1){
- // t: time; l: lyric
- let t_ = tl[0].replace('[','')
- let t = t_.split(':')
- if(t.length > 1){
- let treverse = t.reverse()
- let ts = 0
- if (treverse[0].indexOf('.') != -1){
- // 毫秒以小数形式放在了秒上面
- for (let j=0; j<treverse.length;j++){
- ts += Number(treverse[j]) * 60 ** (j)
- }
- }
- else{
- // 毫秒单独放置
- for (let j=0; j<treverse.length;j++){
- if (j == 0){
- ts += Number(treverse[j]) / 1000
- }
- else{
- ts += Number(treverse[j]) * 60 ** (j - 1)
- }
- }
- }
- ts = ts.toFixed(2)
- let l = tl.splice(1,1000).join(']').trim()
- if (l.length > 0){
- lrcj.times.push(ts)
- lrcj.lrcs.push(l)
- }
- }
- else{
- let l = tl.splice(1,1000).join(']').trim()
- if (l.length > 0){
- lrcj.times.push(Number(t_).toFixed(2))
- lrcj.lrcs.push(l)
- }
- }
-
- }
- }
- }
- for (let i=0;i<this.spaceLineNum;i++){
- lrcj.lrcs.push('')
- lrcj.lrcs.splice(0,0,'')
- }
- return lrcj
- },
- }
- }
- </script>
- <style>
- .lyric-main {
- position: relative;
- z-index: 10;
- background-repeat:no-repeat;
- background-size:100% 100%;
- -moz-background-size:100% 100%;
- }
- .lyric-show {
- position: absolute;
- overflow-anchor: none;
- }
- .lyric-show ::-webkit-scrollbar{
- display: none;
- width: 0 !important;
- height: 0 !important;
- -webkit-appearance: none;
- background: transparent;
- }
- .lyric-show text{
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-around;
- text-align: center;
- white-space: nowrap;
- height: 32px;
- font-size: 16px;
- overflow: auto;
- }
- .center-view {
- position: absolute;
- display: flex;
- flex-direction: row;
- align-items: center;
- white-space: nowrap;
- width: 100%;
- opacity: 0.6;
- overflow: visible;
- pointer-events: none;
- }
- .center-btn{
- position: absolute;
- left: 0;
- width: 20%;
- text-align: left;
- padding: 0 5px;
- color: #ffffff;
- pointer-events: all;
- }
- .center-btn:active{
- opacity: 0.6;
- }
- .center-line {
- position: absolute;
- left: 20%;
- width: 60%;
- height: 1px;
- background-color: #ffffff;
- pointer-events: none;
- }
- .center-time{
- position: absolute;
- right: 0;
- width: 20%;
- text-align: right;
- padding: 0 5px;
- color: #ffffff;
- }
- .check{
- position: absolute;
- float: left;
- padding: 0 5px;
- opacity: 0.5;
- }
- .selectControl{
- position: absolute;
- top: 0;
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-around;
- width: 100%;
- background-color: grey;
- }
- .selectControl view:active{
- opacity: 0.8;
- }
- .selectControl view{
- margin: 5px 0;
- padding: 5px 2px;
- width: 30%;
- overflow: hidden;
- white-space: nowrap;
- text-align: center;
- font-size: 16px;
- border-radius: 30px;
- background-color: grey;
- }
- </style>
|