Let’s take a look at the UI renderings



It looks complicated! First of all, the components can be roughly divided into the following four elements without the circular progress bar and the following elements

  • The cover
  • Play progress bar
  • Play button
  • The list of

Good! Setting up a lu

Forget about the Vue build project, I’m going to use the Vuetify component library

  • HTML part

<v-card class="r_card play_item" dark>                <audio controls ref="audio" id="audio" hidden>                    <source :src="m" type="audio/mpeg" />                </audio>                <div class="player_inner" :class="{play_ing: play}">                    <v-system-bar dark color="transparent" class="v_system_bar">                        <v-spacer></v-spacer>                        <v-icon class="mr-2">mdi-wifi-strength-4</v-icon>                        <v-icon class="mr-2">mdi-signal-cellular-outline</v-icon>                        <v-icon class="mr-2">mdi-battery</v-icon>                        <span>12:30</span>                    </v-system-bar>                    <div class="image" :class="{image_in: listOut, image_out: listIn,}">                        <div class="image_wrap">                            <img                                class="img"                                src="http://akveo.com/ngx-admin/assets/images/cover2.jpg"                            />                        </div>                    </div>                    <div class="slder_bar">                        <v-slider v-model="slider" class="align-center ml-5 mr-30" hide-details />                    </div>                    <div class="icon_play" :class="{icon_play_in: listOut, icon_play_out: listIn,}">                        <v-btn fab dark color="purple" @click="onPlayPause" v-if="play">                            <v-icon dark class="mdi-36px">mdi-roman-numeral-2</v-icon>                        </v-btn>                        <v-btn fab dark color="purple" @click="onPlay" v-else>                            <v-icon dark class="mdi-36px">mdi-play</v-icon>                        </v-btn>                    </div>                    <v-list                        three-line                        v-if="listVisible"                        class="v-list"                        :class="{v_list_in: listIn, v_list_out: listOut,}"                    >                        <v-subheader inset>My playlists</v-subheader>                        <v-list-item                            v-for="(item, key) in items"                            :key="key"                            :class="'v-list-item-' + (key+1)"                            @click="() = > {}"                        >                            <v-list-item-avatar>                                <v-img :src="item.avatar"></v-img>                            </v-list-item-avatar>                            <v-list-item-content>                                <v-list-item-title>{{ item.title }}</v-list-item-title>                                <v-list-item-subtitle>{{ item.subtitle }}</v-list-item-subtitle>                            </v-list-item-content>                            <v-list-item-icon>08:34</v-list-item-icon>                        </v-list-item>                    </v-list>                </div>            </v-card>Copy the code

  • JS part

import m from '@/assets/Jain - Lil Mama.mp3';export default {    data() {        return {            m: m,            play: false// Play state listVisible:true,            listIn: false// Enter the animation listOut:false, // List disappeared animation Slider: 0,}; }, mixins: [echartMixins], watch: { play:function(bool) {            if (bool) {                this.listOut = true;                setTimeout(() => {                    this.listOut = false;                    this.listVisible = false;                }, 500);            } else {                this.listVisible = true;                this.listIn = true;                setTimeout(() => {                    this.listIn = false;                                    }, 1000);            }                    }       },    methods: {        handlePieBoxVisibleStatus() { this.pieBoxVisible = ! this.pieBoxVisible; },handleRunReversal() { this.reversal = ! this.reversal; }, handleZoomCamera(url) { this.url = url; this.reversal =true;        },        onPlay() {            this.$refs.audio.play();                this.play = true;            },        onPlayPause() {            this.$refs.audio.pause();                this.play = false; }}};Copy the code

  • The CSS part

.player_inner {                position: relative;                height: 665px;                .v_system_bar {                    position: absolute;                    top: 0;                    left: 0;                    width: 100%;                    z-index: 3;                }                .image {                    height: 0;                    padding-top: 260px;                    width: 100%;                    left: 0;                    top: 0;                    border-radius: 0;                    overflow: hidden;                    position: absolute;                    z-index: 2;                    transition: all 0.5s linear 0.6s;                    .image_wrap {                        position: absolute;                        width: 100%;                        height: 0;                        padding-top: 260px;                        top: 0;                        left: 0;                        .img {                            width: 100%;                            position: absolute;                            top: 50%;                            left: 50%;                            transform: translate(-50%, -50%);                        }                    }                }                .slder_bar {                    width: 100%;                    height: 50px;                    display: flex;                    align-items: center;                    justify-content: center;                    position: absolute;                    top: 210px;                    left: 0;                    background: rgba($color: # FFFFFF, $alpha: 0.2); z-index: 3; opacity: 1; The transition: all 0.3 s; .mr-30 { margin-right: 100px; } } .icon_play { position: absolute; right: 20px; top: 232px; z-index: 3; // Transition: all 0.3s linear; } .v-list { padding-top: 260px; margin-bottom: 20px; } .v_list_in { @for $i from 1 through 4 { .v-list-item-#{$i} { opacity: 0; Animation: itemSlideIn 0.4s linear #{$I / 20}s; animation-fill-mode: forwards; } } } .v_list_out { @for $i from 1 through 4 { .v-list-item-#{4 + 1 - $i} { animation: SlideOutDown 0.4s linear #{$I / 35}s; animation-fill-mode: forwards; }}. Icon_play_in {animation: iconPlay 0.5s linear; animation-fill-mode: forwards; }. Icon_play_out {animation: iconPlayPause 0.5s linear; animation-fill-mode: forwards; }. Image_in {animation: imagePlay 0.3s linear; animation-fill-mode: forwards; }. Image_out {animation: imagePlayPause 0.5s Linear; animation-fill-mode: forwards; } } .play_ing { .image { top: 50%; The transform: translate (0, 50%) scale (0.6); border-radius: 50%; height: 0; padding-top: 100%; .image_wrap { animation: tyggxh 6s linear 0s infinite; height: 0; padding-top: 100%; .img { height: 100%; width: auto; } } } .slder_bar { opacity: 0; } .icon_play { right: calc(100% / 2 - 28px); top: calc(100% / 2 - 28px); }}@keyframes itemSlideIn {from {transform: translate3d(0, 400%, 0); visibility: visible; opacity: 0; } to { transform: translate3d(0, 0, 0); opacity: 1; }} @keyframes slideOutDown {0% {transform: translateZ(0); opacity: 1; } to { visibility: hidden; transform: translate3d(0, 400%, 0); opacity: 0; }} @keyframes iconPlay {0% {right: 20px; top: 232px; } 15% { right: 20px; top: 230px; } 30% { right: 20px; top: 234px; } 45% { right: 20px; top: 232px; } 60% { right: 20px; top: 232px; } 100% { right: calc(100% / 2 - 28px); top: calc(100% / 2 - 28px); }} @keyframes iconPlayPause {0% {right: calc(100% / 2-28px); top: calc(100% / 2 - 28px); } 15% { right: calc(100% / 2 - 28px); top: calc(100% / 2 - 30px); } 30% { right: calc(100% / 2 - 28px); top: calc(100% / 2 - 26px); } 45% { right: calc(100% / 2 - 28px); top: calc(100% / 2 - 28px); } 60% { right: calc(100% / 2 - 28px); top: calc(100% / 2 - 28px); } 100% { right: 20px; top: 232px; }} @keyframes imagePlay {0% {top: 0; transform: translate(0, 0) scale(1); border-radius: 0; height: 0; padding-top: 260px; } 20% { top: 0; transform: translate(0, 0) scale(1); border-radius: 0; height: 0; padding-top: 260px; } 100% { top: 50%; The transform: translate (0, 50%) scale (0.6); border-radius: 50%; height: 0; padding-top: 100%; }} @keyframes imagePlayPause {0% {top: 50%; The transform: translate (0, 50%) scale (0.6); border-radius: 50%; height: 0; padding-top: 100%; } 60% { top: 0; transform: translate(0, 0) scale(1); border-radius: 50%; height: 0; padding-top: 100%; } 70% { left: 0; top: 0%; transform: translate(0, 0) scale(1); border-radius: 50%; padding-top: 100%; } 95% { left: 0; top: 0; border-radius: 0; padding-top: 100%; } 100% { width: 100%; top: 0; border-radius: 0; padding-top: 260px; }}Copy the code

It looks like we can achieve 60% of the effect

  • Realize music playback pause effect
  • Realize list push display, push down disappear effect
  • Button click beat effect, position transformation effect
  • Cover rotation effect, change the effect of large and small

Click here to see the effect

GITHUB project address

Github.com/Groundhog-C…