使用 Stimulus 实现悬停视频预览
Source: Dev.to
演示文稿索引
录制完演示文稿后,需要一种方式来浏览它们。一个简单的索引页面会列出所有演示文稿及其视频缩略图。当鼠标悬停在缩略图上时,视频会播放预览。将光标移开后,视频会恢复为海报图像。
preview#play mouseleave->preview#pause"
}
%>Active Storage 的 representable? 方法会检查是否可以生成预览,而 representation() 会自动创建缩略图。
预览控制器
所有逻辑都在一个 Stimulus 控制器中完成:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = {
segments: { type: Number, default: 3 },
interval: { type: Number, default: 1000 },
minDuration: { type: Number, default: 5 }
}
connect() {
this.originalTime = 0
this.wasPlaying = false
this.previewTimer = null
this.currentIndex = 0
this.isReady = false
this.timestamps = []
this.element.addEventListener("loadedmetadata", () => {
this.#calculateTimestamps()
this.isReady = true
})
}
}理解 loadedmetadata
loadedmetadata 事件是关键。它在浏览器加载足够的视频数据以获取时长、尺寸以及其他元数据时触发。如果没有这些信息,就无法计算有意义的预览时间戳。
this.element.addEventListener("loadedmetadata", () => {
this.#calculateTimestamps()
this.isReady = true
})只有在 loadedmetadata 触发后,才能可靠地访问 this.element.duration。在此事件之前尝试使用它会得到 NaN 或 0。
智能时间戳
控制器不会仅仅从开头播放,而是展示视频的不同片段:
#calculateTimestamps() {
const duration = this.element.duration
if (duration 1) {
this.previewTimer = setInterval(() => {
this.#showNextTimestamp()
}, this.intervalValue)
}
}当你悬停时,它会保存当前状态、静音并开始循环播放预览片段。离开时,它会把一切恢复到原来的状态。
自动循环预览
预览会自动循环遍历计算出的时间戳:
#showNextTimestamp() {
this.element.currentTime = this.timestamps[this.currentIndex]
this.element.play()
this.currentIndex = (this.currentIndex + 1) % this.timestamps.length
}取模运算符 (%) 用来实现循环:当到达最后一个片段时,会回到第一个。配合 setInterval,就形成了一个循环预览,让用户真实感受到视频内容。
关键在于等待正确的时机(元数据加载完成),计算智能的预览点,并在交互结束时妥善清理。这个小巧的 Stimulus 控制器展示了几行 JavaScript 如何提供精致的用户体验。 🥳