1
0
Fork 0
mirror of https://github.com/heyarne/airsonic-ui.git synced 2026-05-07 02:33:39 +02:00

Implement progress bar with html elements, fixes #39

This commit is contained in:
Arne Schlüter 2019-01-22 22:20:43 +01:00
commit a9fbe5c741
2 changed files with 81 additions and 79 deletions

View file

@ -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"])]]))

View file

@ -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