ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] #### Vue 仿音乐歌手列表组件 ![](https://box.kancloud.cn/cc22fe0125aa36920e09aa3445c404a0_376x669.png) * [ ] 实现代码 js ~~~ <template> <div class="music-list"> <!-- 返回按钮 --> <div class="back" @click="back"> <i class="icon-back"></i> </div> <!-- 歌手标题 --> <h1 class="title" v-html="title"></h1> <!-- 背景 注意以下两个属性 --> <!--transform-origin: top;--> <!--background-size: cover;--> <div class="bg-image" :style="bgStyle" ref="bgImage"> <div class="play-wrapper"> <div ref="playBtn" v-show="songs.length>0" class="play" @click="random"> <i class="icon-play"></i> <span class="text">随机播放全部</span> </div> </div> <div class="filter" ref="filter"></div> </div> <!-- 背景遮罩层 --> <div class="bg-layer" ref="layer"></div> <!-- 歌手列表 --> <scroll :data="songs" @scroll="scroll" :listen-scroll="listenScroll" :probe-type="probeType" class="list" ref="list"> <div class="song-list-wrapper"> <song-list :songs="songs" :rank="rank" @select="selectItem"></song-list> </div> <!-- loading 组件 --> <div v-show="!songs.length" class="loading-container"> <loading></loading> </div> </scroll> </div> </template> <script type="text/ecmascript-6"> import Scroll from 'base/scroll/scroll' import Loading from 'base/loading/loading' import SongList from 'base/song-list/song-list' import { prefixStyle } from 'common/js/dom' import { playlistMixin } from 'common/js/mixin' import { mapActions } from 'vuex' const RESERVED_HEIGHT = 40 const transform = prefixStyle('transform') const backdrop = prefixStyle('backdrop-filter') export default { mixins: [playlistMixin], props: { bgImage: { type: String, default: '' }, songs: { type: Array, default () { return [] } }, title: { type: String, default: '' }, rank: { type: Boolean, default: false } }, data () { return { scrollY: 0 } }, computed: { bgStyle () { return `background-image:url(${this.bgImage})` } }, created () { this.probeType = 3 this.listenScroll = true }, mounted () { // 获取背景图高度 this.imageHeight = this.$refs.bgImage.clientHeight // 最小滚动距离 this.minTransalteY = -this.imageHeight + RESERVED_HEIGHT // 列表距离顶部偏移 = 背景图实际高度 this.$refs.list.$el.style.top = `${this.imageHeight}px` }, methods: { handlePlaylist (playlist) { const bottom = playlist.length > 0 ? '60px' : '' this.$refs.list.$el.style.bottom = bottom this.$refs.list.refresh() }, scroll (pos) { this.scrollY = pos.y }, back () { this.$router.back() }, selectItem (item, index) { this.selectPlay({ list: this.songs, index }) }, random () { this.randomPlay({ list: this.songs }) }, ...mapActions([ 'selectPlay', 'randomPlay' ]) }, watch: { // 监听 scroll 滚动距离 scrollY (newVal) { // 歌手列表最大滚动距离 let translateY = Math.max(this.minTransalteY, newVal) // 背景图缩放比例 let scale = 1 // 背景图zIndex let zIndex = 0 // 背景图虚化值 let blur = 0 const percent = Math.abs(newVal / this.imageHeight) // 歌手列表下拉 if (newVal > 0) { scale = 1 + percent zIndex = 10 } else { blur = Math.min(20, percent * 20) } // 歌手列表跟随滚动 this.$refs.layer.style[transform] = `translate3d(0,${translateY}px,0)` // 背景图渐变 this.$refs.filter.style[backdrop] = `blur(${blur}px)` // 歌手列表上拉 if (newVal < this.minTransalteY) { zIndex = 10 this.$refs.bgImage.style.paddingTop = 0 this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px` this.$refs.playBtn.style.display = 'none' } else { // 歌手列表下拉 this.$refs.bgImage.style.paddingTop = '70%' this.$refs.bgImage.style.height = 0 this.$refs.playBtn.style.display = '' } // 背景图缩放和 zIndex 改变 this.$refs.bgImage.style[transform] = `scale(${scale})` this.$refs.bgImage.style.zIndex = zIndex } }, components: { Scroll, Loading, SongList } } </script> <style scoped lang="stylus" rel="stylesheet/stylus"> @import "~common/stylus/variable" @import "~common/stylus/mixin" .music-list position: fixed z-index: 100 top: 0 left: 0 bottom: 0 right: 0 background: $color-background .back position absolute top: 0 left: 6px z-index: 50 .icon-back display: block padding: 10px font-size: $font-size-large-x color: $color-theme .title position: absolute top: 0 left: 10% z-index: 40 width: 80% no-wrap() text-align: center line-height: 40px font-size: $font-size-large color: $color-text .bg-image position: relative width: 100% height: 0 padding-top: 70% transform-origin: top background-size: cover .play-wrapper position: absolute bottom: 20px z-index: 50 width: 100% .play box-sizing: border-box width: 135px padding: 7px 0 margin: 0 auto text-align: center border: 1px solid $color-theme color: $color-theme border-radius: 100px font-size: 0 .icon-play display: inline-block vertical-align: middle margin-right: 6px font-size: $font-size-medium-x .text display: inline-block vertical-align: middle font-size: $font-size-small .filter position: absolute top: 0 left: 0 width: 100% height: 100% background: rgba(7, 17, 27, 0.4) .bg-layer position: relative height: 100% background: $color-background .list position: absolute top: 0 bottom: 0 width: 100% background: $color-background .song-list-wrapper padding: 20px 30px .loading-container position: absolute width: 100% top: 50% transform: translateY(-50%) </style> ~~~