學習到的知識點 HTML
CSS
linear-gradient
animation
animation-play-state
::after 偽元素
z-index
JS
Jquery
clientX,screenX,pageX
favicon
簡介 Demo
一個有唱盤會旋轉的音樂播放器。
HTML
音樂訊息與 Progrees info 包住整個結構的.container
,與存放 progress bar。
1 2 3 4 5 6 7 8 9 10 11 12 <body > <h1 > Music Player</h1 > <div class ="music-container" id ="music-container" > <div class ="music-info" > <h4 id ="title" > </h4 > <div class ="progress-container" id ="progress-container" > <div class ="progress" id ="progress" > </div > </div > </div > <audio id ="audio" src ="music/smallguitar.mp3" > </audio > </div > </body >
圖片容器.img-container
,還有三個從Font-awasome 網站引入的撥放、倒退、快轉鍵。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <div class ="img-container" > <img src ="images/smallguitar.jpg" alt ="music-cover" id ="cover" /> </div > <div class ="navigation" > <button id ="prev" class ="action-btn" > <i class ="fas fa-backward" > </i > </button > <button id ="play" class ="action-btn action-btn-big" > <i class ="fas fa-play" > </i > </button > <button id ="next" class ="action-btn" > <i class ="fas fa-forward" > </i > </button > </div >
CSS 將所有元素置中
背景漸層的效果
使用linear-gradient
,在 Amos 切版也有使用到。
1 2 3 4 5 6 body { background-image : linear-gradient ( 0deg , rgba (247 , 247 , 247 , 1 ) 23.8% , rgba (252 , 221 , 221 , 1 ) 92% );
這部分的 Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @import url("https://fonts.googleapis.com/css?family=Lato&display=swap" ); * { box-sizing : border-box; }body { background-image : linear-gradient ( 0deg , rgba (247 , 247 , 247 , 1 ) 23.8% , rgba (252 , 221 , 221 , 1 ) 92% ); height : 100vh ; display : flex; flex-direction : column; align-items : center; justify-content : center; font-family : "Lato" , sans-serif; margin : 0 ; }
外層容器
img-container
relative
可以定位到上層元素.music-container
,而不會定位到最外層導致跑版。
1 2 3 4 .img-container { position : relative; width : 110px ; }
這部分的 Code
1 2 3 4 5 6 7 8 9 10 .music-container { background-color : #fff ; border-radius : 15px ; box-shadow : 0 20px 20px 0 rgba (252 , 169 , 169 , 0.6 ); display : flex; padding : 20px 30px ; position : relative; margin : 100px 0 ; z-index : 10 ; }
設置音樂圖片
圖片設置跟外容器一樣寬
使用width: inherit
,瀏覽器會將上層元素img-container
的寬度賦值給他,就不需要再img-container
上設定position:relative
。
參考文章
定位照片
使用絕對定位
1 2 3 4 5 { position : absolute; bottom : 0 ; left : 0 ; }
animation
animation: 動畫名稱 | 動畫持續時間 | 動畫加速度函式 | 動畫播放次數
animation
: animation-name
|name-duration
| animation-timing-function
| animation-iteration-count
animation-play-state
為動畫播放或暫停狀態。 running:預設值,表示動畫運行。 paused:表示動畫暫停
1 2 3 4 { animation : rotate 3s linear infinite; animation-play-state : paused; }
音樂播放時開始旋轉
這部分是由 JS 控制,按下播放音樂的 Button,才將.play 的 class 加上去。
1 2 3 .music-container .play .img-container img { animation-play-state : running; }
這部分的 Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .img-container { position : relative; width : 110px ; }.img-container img { border-radius : 50% ; object-fit : cover; height : 110px ; width : inherit; position : absolute; bottom : 0 ; left : 0 ; animation : rotate 3s linear infinite; animation-play-state : paused; }.music-container .play .img-container img { animation-play-state : running; }
圖片上的白點 將圖片上的白點移到中間,使用到偽元素::after
。
1 2 3 4 5 6 7 8 9 10 11 .img-container ::after { content : "" ; background-color : #fff ; border-radius : 50% ; position : absolute; bottom : 100% ; left : 50% ; width : 20px ; height : 20px ; transform : translate (-50% , 50% ); }
設定動畫 1 2 3 4 5 6 7 8 9 @keyframes rotate { from { transform : rotate (0deg ); } to { transform : rotate (360deg ); } }
播放欄
去除點擊後出現的框框
1 2 3 .action-btn :focus { outline : 0 ; }
這部分的 Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 .navigation { display : flex; align-items : center; justify-content : center; z-index : 1 ; }.action-btn { background-color : #fff ; border : 0 ; color : #dfdbdf ; font-size : 20px ; cursor : pointer; padding : 10px ; margin : 0 20px ; }.action-btn .action-btn-big { color : #cdc2d0 ; font-size : 30px ; }.action-btn :focus { outline : 0 ; }
Music Info 要做出 pop up 的效果(按下播放時,才出現),所以一開始要設定transform: translateY(0%)
,opacity: 0
,不讓 Info 面板出現。
transform
的座標別於我們一般認知的座標,以下面這張圖為例,右下在一般認知裡為(100,-100),但transform
原點在左上,所以往他的右下為(100,100),相反的往他的左上為(-100,-100)。
這邊使用到transition:ease-in
效果,意思為緩慢的開始。
width: calc(100% - 40px)
,100%為上層元素的最大寬度,calc()
為動態計算,不用在一個一個計算現在的寬度為多少,可以和max()、min()、clamp
或是 CSS 的變數互相搭配,中間是減號,前後要留兩個空格。
完整的 Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .music-info { background-color : rgba (255 , 255 , 255 , 0.5 ); border-radius : 15px 15px 0 0 ; position : absolute; top : 0 ; left : 20px ; width : calc (100% - 40px ); padding : 10px 10px 10px 150px ; opacity : 0 ; transform : translateY (0% ); transition : transform 0.3s ease-in, opacity 0.3s ease-in; z-index : 0 ; }.music-container .play .music-info { opacity : 1 ; transform : translateY (-100% ); }.music-info h4 { margin : 0 ; }
Progress .progress
裡面的width
改變他的長度,會使用 Js 動態改變。
transition:新增效果的CSS屬性
| 效果的持續時間
| 動畫執行的計算方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .progress-container { background : #fff ; border-radius : 5px ; cursor : pointer; margin : 10px 0 ; height : 4px ; width : 100% ; }.progress { background-color : #fe8daa ; border-radius : 5px ; height : 100% ; width : 0% ; transition : width 0.1s linear; }
JS 新增變數
JS
1 2 3 4 5 6 7 8 9 10 const musicContainer = document .getElementById ("music-container" );const playBtn = document .getElementById ("play" );const prevBtn = document .getElementById ("prev" );const nextBtn = document .getElementById ("next" );const audio = document .getElementById ("audio" );const progress = document .getElementById ("progress" );const progressContainer = document .getElementById ("progress-container" );const title = document .getElementById ("title" );const cover = document .getElementById ("cover" );
Jquery
直接選取元素
1 2 3 4 5 6 7 8 9 $("#musicContainer" ) $("#playBtn" ) $("#prevBtn" ) $("#nextBtn" ) $("#audio" ) $("#progress" ) $("#progressContainer" ) $("#title" ) $("#cover" )
宣告歌曲陣列,檢索,呼叫loadSong()
設定以歌為 id 的陣列
JS 和 Jquery 都要宣告
1 2 3 4 5 6 7 8 9 10 11 const songs = ["energy" , "inspire" , "smallguitar" ];let songIndex = 0 ;loadSong (songs[songIndex]);
載入歌曲loadSong()
將歌曲的名字,音檔,照片讀入。
JS
1 2 3 4 5 6 7 function loadSong (song ) { title.innerText = song; audio.src = `music/${song} .mp3` ; cover.src = `images/${song} .jpg` ; }
Jquery
audio.src =
轉成$("#audio").attr()
1 2 3 4 5 function loadSong (song ) { $("#title" ).Text (song); $("#audio" ).attr (`music/${song} .mp3` ); $("#cover" ).attr (`images/${song} .jpg` ); }
播放按鈕的事件監聽
JS
1 2 3 4 5 6 7 8 9 10 playBtn.addEventListener ("click" , () => { const isPlaying = musicContainer.classList .contains ("play" ); if (isPlaying) { pauseSong (); } else { playSong (); } });
Jquery
playBtn.addEventListener("click"
轉成 $("#playBtn").click(function(){
.classList.contains
轉成.hasClass
1 2 3 4 5 6 7 8 $("#playBtn" ).click (function ( ){const isPlaying =$("musicContainer" ).hasClass ("play" ); if (isPlaying true ) { pauseSong (); } else { playSong (); } });
播放歌曲 playSong()
,暫停歌曲pauseSong()
播放和暫停功能一樣,只有 icon 交換和audio.play()
,audio.pause()
不同。
JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function playSong ( ) { musicContainer.classList .add ("play" ); playBtn.querySelector ("i.fas" ).classList .remove ("fa-play" ); playBtn.querySelector ("i.fas" ).classList .add ("fa-pause" ); audio.play ();function pauseSong ( ) { musicContainer.classList .remove ("play" ); playBtn.querySelector ("i.fas" ).classList .add ("fa-play" ); playBtn.querySelector ("i.fas" ).classList .remove ("fa-pause" ); audio.pause (); }
Jquery
從 id 去找到 i,再將圖檔替換掉。
playBtn.querySelector("i.fas")
轉成 $("#play").find("i").attr("class", "fas fa-pause")
audio.play()
轉成$("#audio")[0].play();
.play
跟.pause
都是 DOM 的操作方法。$
queryselector 為 jquery 物件而不是原始的 DOM 元素,所以我們需要成 $("#audio")[0].play()
。
一開始不理解這種用法,搜尋了一下後得到解答,意思為: 利用 Jquery 選擇器,得到 id 為 audio 的第一個 DOM 對象。
1 2 3 4 5 6 7 8 9 10 11 function playSong ( ) { $("#music-container" ).addClass ("play" ); $("#play" ).find ("i" ).attr ("class" , "fas fa-pause" ); $("#audio" )[0 ].play (); }function pauseSong ( ) { $("#music-container" ).removeClass ("play" ); $("#play" ).find ("i" ).attr ("class" , "fas fa-play" ); $("#audio" )[0 ].pause (); }
按下前一首prevSong()
、下一首歌nextSong()
JS
利用songIndex
,控制歌曲的撥放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function nextSong ( ) { songIndex++; if (songIndex > songs.length - 1 ) { songIndex = 0 ; } loadSong (songs[songIndex]); playSong (); }function prevSong ( ) { songIndex--; if (songIndex < 0 ) { songIndex = songs.length - 1 ; } loadSong (songs[songIndex]); playSong (); } prevBtn.addEventListener ("click" , prevSong); nextBtn.addEventListener ("click" , nextSong);
Jquery
prevBtn.addEventListener("click", prevSong)
轉成$("#next").click(function(){nextSong();})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function nextSong ( ) { songIndex++; if (songIndex > songs.length - 1 ) { songIndex = 0 ; } loadSong (songs[songIndex]); playSong (); }function prevSong ( ) { songIndex--; if (songIndex < 0 ) { songIndex = songs.length - 1 ; } loadSong (songs[songIndex]); playSong (); } $("#pre" ).click (function ( ){ prevSong (); }); $("#next" ).click (function ( ){ nextSong (); });
進度條
JS
duration
是整首歌的時間,currentTime
是過了多久。
event.offsetX、event.offsetY
,滑鼠相對於事件源元素(srcElement)的 X,Y 坐標。
JS 一秒區分 clientX,offsetX,screenX,pageX 之間關係
Audio duration Property
1 2 3 4 5 6 7 8 9 10 11 12 13 function updateProgress (e ) { const { duration, currentTime } = e.srcElement ; const progressPercent = (currentTime / duration) * 100 ; progress.style .width = `${progressPercent} %` ; } audio.addEventListener ("timeupdate" , updateProgress);
Jquery
.on 就是 addEventListener
audio.addEventListener()
轉成$("#audio").on()
const { duration, currentTime } = e.srcElement
轉成const duration = $("#audio")[0].duration
progress.style.width = ()
轉成$("#progress").css
1 2 3 4 5 6 $("#audio" ).on ("timeupdate" , function ( ) { const duration = $("#audio" )[0 ].duration ; const progressPercent = $("#audio" )[0 ].currentTime ; const progressPercent = (currentTime / duration) * 100 ; $("#progress" ).css (width, `${progressPercent} %` ); });
可以點選進度條
JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function setProgress (e ) { const width = this .clientWidth ; const clickX = e.offsetX ; const duration = audio.duration ; audio.currentTime = (clickX / width) * duration; } progressContainer.addEventListener ("click" , setProgress);
Jquery
這邊有兩種寫法:
照著原作者方式改寫
參考別人寫法
Bug 這邊的 width 抓取要使用#progress-container
,如果使用e.target
會導致這種錯誤。目前還不太明白這個錯誤是甚麼意思,待了解…
目前使用$("#progress-container")
可以解決。
使用 clientX 與 width 取百分比
1 2 3 4 5 6 7 $("#progress-container" ).click (function (e ) { const duration = $("#audio" )[0 ].duration ; const width = $("#progress-container" ).width (); const clickX = e.offsetX ; const percent = (clickX / width) * duration; $("#audio" )[0 ].currentTime = percent; });
使用到.offset().left
this.clientWidth
轉成$('#progress-container').width()
1 2 3 4 5 6 7 8 9 10 11 12 13 $('#progress-container' ).click (function (e ) { const width = $('#progress-container' ).width () const offsetX = $(e.target ).offset ().left ; const clickX = e.pageX const duration = $('#audio' )[0 ].duration ; const percent = ((clickX - offsetX) / width) $('#audio' )[0 ].currentTime = percent * duration });
播放完畢後,自動跳下一首
JS
1 audio.addEventListener ("ended" , nextSong);
Jquery
audio.addEventListener()
轉成$("audio").on
1 2 3 $("audio" ).on ("ended" ,function ( ){ nextSong (); })
遇到的問題
1
Bug
在輸入 commit 時,少加了結束的引號,而出現了 dquite>的這串文字,按^Z 也無效,最後 Google 一下,找到解法 。
2
Bug 使用 Chrome 會出現此錯誤,但使用 edge 和 Firefox 時不會。
解決方法是 ,在 index.html 加上<link rel="shortcut icon" href="#" />
但是,favicon 是什麼 ?
它的是全名是 Favorite Icon,就是網頁旁邊出現的小圖案。
參考文章 The Embed Audio element
CSS width:100%和 width:inherit
完整解析 CSS 動畫 ( CSS Animation )
CSS transition 各種速率
CSS 沒有極限 - CSS 的神奇 Calc 運算
css3 transition 原理
Javascript jQuery attr() Method
原生 JS 替代 jQuery 的各種方法彙總
JS 一秒區分 clientX,offsetX,screenX,pageX 之間關係
Audio duration Property