Skip to content

Instantly share code, notes, and snippets.

@NickHatBoecker
Last active May 5, 2021 19:54
Show Gist options
  • Save NickHatBoecker/84a8c8514b486370c1aece4b02c3ae7a to your computer and use it in GitHub Desktop.
Save NickHatBoecker/84a8c8514b486370c1aece4b02c3ae7a to your computer and use it in GitHub Desktop.
// src/components/Newsticker.vue
<script>
export default {
props: {
text: { type: String, required: true },
},
data: () => ({
elementId: 'newsticker',
elementWrapperId: 'newstickerWrapper',
animationFrame: null,
boxWidth: 0,
resetPosition: null,
lastUpdate: null,
position: 0,
pixelPerUpdate: 2, // Pixelanzahl die der Text bei jedem Update verschoben wird
timeout: 20, // Millisekunden eher das nächste Update getriggert wird
paused: false, // Startet die Animation direkt beim Aufruf
}),
mounted () {
this.startAnimation()
},
destroyed () {
// Wir entfernen hier den Listener, den wir im mounted Hook registriert haben.
// So stellen wir sicher, dass er bspw. bei einem Routen-Wechsel nicht weiter ausgeführt wird.
this.stopAnimation()
},
methods: {
toggleAnimation () {
if (this.paused) {
this.startAnimation()
} else {
this.stopAnimation()
}
this.paused = !this.paused
},
startAnimation () {
// Statt den bekannteren Funktionen "setTimeout" und "setInterval" nutzen wir hier "requestAnimationFrame".
// Diese Funktion stoppt automatisch wenn der Browser minimiert ist oder der Browser-Tab nicht im Fokus ist.
// Das schont CPU und Akku.
this.animationFrame = window.requestAnimationFrame(this.update)
},
stopAnimation () {
window.cancelAnimationFrame(this.animationFrame)
},
update () {
// Wenn die Animation vom User gestoppt wurde, brechen wir direkt aus.
if (this.paused) return
const now = Date.now()
if (this.lastUpdate && (now - this.lastUpdate) < this.timeout) {
// Wenn der timeout noch nicht erreicht wurde, wird der Text nicht verschoben.
// Wir müssen aber startAnimation() aufrufen um die Schleife weiterlaufen zu lassen.
this.startAnimation()
return
}
this.calculateStartAndResetPositions()
this.updatePosition()
// lastUpdate wird auf den aktuellen Timestamp gesetzt, damit wir ihm im nächsten Update überprüfen können
this.lastUpdate = now
// Mit startAnimation() starten wir den nächsten Schleifenvorgang.
this.startAnimation()
},
/**
* [1] Der Text soll von rechts einlaufen. Wir setzen die Startposition also auf das rechte Ende des Wrappers.
*
* [2] Wenn der Text den linken Rand des Wrappers erreicht hat (Position 0), soll der Text
* zurück an die Startposition gesetzt werden. Dazu errechnen wir die Text-Breite und
*
* [3] setzen die errechnete resetPosition
*/
calculateStartAndResetPositions () {
this.startPosition = this.$refs[this.elementWrapperId].offsetWidth // [1]
const textWidth = this.$refs[this.elementId].clientWidth // [2]
this.resetPosition = 0 - this.textWidth // [3]
},
updatePosition () {
const currentPosition = this.getCurrentPosition()
let newPosition = `${currentPosition - this.pixelPerUpdate}px`
if (currentPosition <= this.resetPosition) {
// Der Text hat die in updateWidths errechnete End-Position erreicht.
// Also setzen wir ihn wieder an die Startposition.
newPosition = `${this.startPosition}px`
}
this.$refs[this.elementId].style.left = newPosition
},
getCurrentPosition () {
let currentPosition = this.$refs[this.elementId].style.left
currentPosition = parseInt(currentPosition.replace('px', ''), 10)
if (!currentPosition) {
// currentPosition ist noch nicht gesetzt. Wir müssen also den Start festlegen.
currentPosition = this.startPosition
}
return currentPosition
},
},
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment