diff --git a/src/cljs/airsonic_ui/components/audio_player/views.cljs b/src/cljs/airsonic_ui/components/audio_player/views.cljs index bcc1670..79d951b 100644 --- a/src/cljs/airsonic_ui/components/audio_player/views.cljs +++ b/src/cljs/airsonic_ui/components/audio_player/views.cljs @@ -1,81 +1,34 @@ (ns airsonic-ui.components.audio-player.views (:require [re-frame.core :refer [subscribe dispatch]] [airsonic-ui.routes :as routes] - [airsonic-ui.components.highres-canvas.views :refer [canvas]] [airsonic-ui.helpers :refer [add-classes muted-dispatch]] [airsonic-ui.views.cover :refer [cover]] [airsonic-ui.views.icon :refer [icon]])) ;; currently playing / coming next / audio controls... -;; FIXME: Sometimes items don't have a duration - -(def progress-bar-color "rgb(93,93,93)") -(def progress-bar-color-buffered "rgb(143,143,143)") -(def progress-bar-color-active "whitesmoke") - -(defn draw-progress [ctx current-time buffered duration] - (let [width (.. ctx -canvas -clientWidth) - height (.. ctx -canvas -clientHeight) - padding 5 - buffered-x (+ padding (* (- width (* 2 padding)) (min 1 (/ buffered duration)))) - current-x (+ padding (* (- width (* 2 padding)) (min 1 (/ current-time duration))))] - ;; vertically center everything - (.translate ctx 0.5 (+ (Math/ceil (/ height 2)) 0.5)) - ;; draw complete bar - (set! (.-strokeStyle ctx) progress-bar-color) - (doto ctx - (.beginPath) - (.moveTo padding 0) - (.lineTo (- width (* 2 padding)) 0) - (.stroke)) - ;; draw the buffered part - (set! (.-strokeStyle ctx) progress-bar-color-buffered) - (doto ctx - (.beginPath) - (.moveTo padding 0) - (.lineTo buffered-x 0) - (.stroke)) - ;; draw the part that's already played - (set! (.-strokeStyle ctx) progress-bar-color-active) - (doto ctx - (.beginPath) - (.moveTo padding 0) - (.lineTo current-x 0) - (.stroke)) - ;; draw a dot marking the current time - (set! (.-fillStyle ctx) progress-bar-color-active) - (doto ctx - (.beginPath) - (.arc current-x 0 (/ padding 2) 0 (* Math/PI 2)) - (.fill)))) - -(defn current-progress [current-time buffered duration] - [canvas {:class "current-progress-canvas" - :draw #(draw-progress % current-time buffered duration)}]) - -;; FIXME: It's ugly to have the canvas padding and styling scattered everywhere (sass, drawing code above, and here) (defn seek "Calculates the position of the click and sets current playback accordingly" [ev] - (let [x (- (.. ev -nativeEvent -pageX) - (.. ev -target getBoundingClientRect -left)) - width (- (.. ev -target -nextElementSibling -clientWidth) 10)] ;; <- 10 = 2 * canvas-padding - (dispatch [:audio-player/seek (/ x width)]))) + (let [x-ratio (/ (.. ev -nativeEvent -layerX) + (.. ev -target -parentElement getBoundingClientRect -width))] + (dispatch [:audio-player/seek x-ratio]))) -(defn buffered-part - [buffered duration] - (let [width (min 100 (* (/ buffered duration) 100))] - [:div.buffered-part {:on-click seek - :style {:width (str "calc(" width "% - 1rem - 10px)")}}])) +(defn- ratio->width [ratio] + (str (.toFixed (* 100 ratio) 2) "%")) (defn progress-bar [song status] (let [current-time (:current-time status) buffered (:buffered status) - duration (:duration song)] - [:article.progress-bar - [buffered-part buffered duration] - [current-progress current-time buffered duration]])) + duration (:duration song) + buffered-width (ratio->width (/ buffered duration)) + played-width (ratio->width (/ current-time duration))] + [:article.progress-bar {:aria-hidden "true"} + [:div.complete-song] + [:div.buffered-part {:style {:width buffered-width} + :on-click seek}] + [:div.played-back {:style {:width played-width}} + [:div.played-back-knob]]])) (defn playback-info [song status] [:a.playback-info.media @@ -143,8 +96,8 @@ ;; show song info, controls, progress bar, etc. [:section.audio-interaction [playback-info current-song playback-status] - [playback-controls is-playing?] [progress-bar current-song playback-status] + [playback-controls is-playing?] [playback-mode-controls playlist]] ;; not playing anything [:p.navbar-item.idle-notification "No audio playing"])]])) diff --git a/src/sass/app.sass b/src/sass/app.sass index c4115b6..0fd3710 100644 --- a/src/sass/app.sass +++ b/src/sass/app.sass @@ -18,11 +18,14 @@ +loader // bottom bar +.has-navbar-bottom + padding-bottom: 64px + .audio-player // first clear some of that navigation styling background-color: $dark color: $light - min-height: 0 + min-height: 64px .navbar-menu padding: 0 @@ -37,6 +40,7 @@ // ... or with all the bells and whistles .audio-interaction display: flex + flex-grow: 1 align-items: center .playback-info @@ -45,39 +49,84 @@ flex-grow: 1 color: inherit - .artist-and-title > * - display: inline-block - white-space: nowrap - width: 100% - text-overflow: ellipsis + .media-left + margin-right: .6rem + .artist-and-title + margin-right: .6rem + + .artist, + .song-title + display: block + white-space: nowrap + width: 100% + max-width: 100% + overflow: hidden + text-overflow: ellipsis .progress-bar // hide progress bar on mobile display: none - @include =tablet + +tablet display: block - flex-grow: 1 + flex-grow: 3 position: relative + height: 1rem + + .complete-song, + .buffered-part, + .played-back + height: 1rem + position: absolute + top: 0 + left: 0 + + // these are the actual bars + &:after + content: '' + display: block + height: 1px + position: relative + top: 50% + + .complete-song + width: 100% + + &:after + background: rgb(93,93,93) .buffered-part - position: absolute - top: .5rem - left: calc(.5rem + 5px) - height: 1rem cursor: pointer - .current-progress-canvas - display: block - height: 1rem - width: 100% + &:after + background: rgb(143,143,143) + + .played-back + pointer-events: none + + &:after + background: whitesmoke + + .played-back-knob + position: absolute + width: 5px + height: 5px + left: 100% + top: 50% + margin-left: -2.5px + margin-top: -2.5px + background: whitesmoke + border-radius: 100% // buttons to control current playback and playlist behavior .playback-controls, .playback-mode-controls flex-shrink: 0 - padding: .3rem + padding-right: .6rem + + .playback-controls + padding-left: .6rem // preview card for album or artist listings .preview-card