Skip to content

Instantly share code, notes, and snippets.

@kirked
Created June 19, 2017 22:45
Show Gist options
  • Save kirked/140c2023b610ed5e07b83eea791ce817 to your computer and use it in GitHub Desktop.
Save kirked/140c2023b610ed5e07b83eea791ce817 to your computer and use it in GitHub Desktop.
Scroll an HTML container to its bottom over a period of time (dashboard use, no user input required)
(defn element-with-id
"Return the element with the given ID."
[id]
(js/document.getElementById id))
(defn resolve-element
"Given an ID or a CSS selector or an element, return the first matching document element."
[selector]
(cond
(string? selector) (or (element-with-id selector)
(js/document.querySelector selector))
:else selector))
(defn clear-interval-atom
[*interval]
(when @*interval
(do
(js/window.clearInterval @*interval)
(reset! *interval nil))))
(defn step-and-interval
"Given a distance in pixels, and duration in millis, calculate
the animation step distance and interval frequency, with
a minimum interval of 20ms (50Hz)."
[distance duration]
(let [step (/ (* 20 distance) duration)
[step multiplier] (if (< step 1) [1 (/ 1 step)] [step 1])
interval (* (/ duration distance) multiplier)]
[step interval]))
;; Based on
;; https://github.com/StevenIseki/scroll-element/blob/master/src/index.js
;;
;; This version doesn't compute window offsets,
;; since we're using a fixed 1080p display.
(defn scroll-to-bottom
"Scroll 'container' to the bottom, placing the timer into '*timer'.
optional:
:duration ms scroll duration (default 1000)
:on-done no-arg fn to call when scroll is done."
[container *timer & {:keys [duration on-done] :or {duration 1000}}]
(let [top-of (fn [e] (-> e .getBoundingClientRect .-top))
container (resolve-element container)
target-top (- (.-scrollHeight container) (-> container .getBoundingClientRect .-height))
[step interval] (step-and-interval target-top duration)
*last-top (atom -1)
scroller (fn []
(let [current-top (.-scrollTop container)]
(if (and (< current-top target-top) (< @*last-top current-top))
(do
(set! (.-scrollTop container) (+ current-top step))
(reset! *last-top current-top))
(do
(clear-interval-atom *timer)
(reset! *last-top -1)
(when (fn? on-done)
(on-done))))))]
;(js/console.info "scrolling" (.-id container) "to target-top:" target-top "moving" step "pixels every" interval "ms")
(reset! *timer (js/window.setInterval scroller interval))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment