vue-waveform
Version:
waveform audio player wavesurfer -waveform js html 音频audio波形图
452 lines (424 loc) • 13.7 kB
JavaScript
import {extend} from './util'
export default class Timeline {
params = {
canvas: undefined,
ctx: undefined,
canvasW: 0,
canVansH: 0,
timecell: undefined,
minutes_per_step: [1, 2, 5, 10, 15, 20, 30, 60, 120, 180, 240, 360, 720, 1440], // min/格
graduation_step: 20, //刻度间最小宽度,单位px
hours_per_ruler: 24, //时间轴显示24小时
start_timestamp: 0,
distance_between_gtitle: 80,
zoom: 24,
g_isMousedown: false, //拖动mousedown标记
g_isMousemove: false, //拖动mousemove标记
g_mousedownCursor: null, //拖动mousedown的位置
returnTime: null, //mouseup返回时间
on_before_click_ruler_callback: null
}
constructor(options) {
extend(this.params, options)
if (this.params.canvas) {
this.params.ctx = this.params.canvas.getContext('2d')
this.params.canvasW = this.params.canvas.width
this.params.canvasH = this.params.canvas.height
}
extend(this, this.params)
this.init(this.start_timestamp, this.timecell, false)
}
/**
*
*
* @param {*} start_left_timestamp
* @param {*} time_cell
* @param {*} redraw_flag
* @memberof Timeline
*/
init(start_left_timestamp, time_cell, redraw_flag) {
this.clearCanvas()
this.draw_cell_bg()
this.add_graduations(start_left_timestamp)
this.add_cells(time_cell)
// this.drawLine(0, this.canVansH, this.canvasW, this.canVansH, `rgb(151, 158, 167)`, 1) //底线
this.draw_last_line(start_left_timestamp)
if (!redraw_flag) { //只有第一次进入才需要添加事件
// this.add_events()
}
}
draw_last_line(start_left_timestamp) {
this.drawLine(this.canvasW, 0, this.canvasW, 20, `rgb(64, 196, 255)`, 2) //中间播放点时间线
let time = start_left_timestamp + (this.sec_per_my * 1000)
this.ctx.fillStyle = `rgb(64, 196, 255)`
this.ctx.fillText(this.changeTime(time), this.canvasW - 34, 25)
}
draw_cell_bg() {
this.ctx.fillStyle = `rgba(69, 72, 76, 0.5)`
this.ctx.fillRect(0, 0, this.canvasW, 15)
}
/* eslint-disable */
accDiv(arg1, arg2) {
let t1 = 0
let t2 = 0
let r1
let r2
try{ t1 = arg1.toString().split('.')[1].length }catch(e){}
try{ t2 = arg2.toString().split(".")[1].length }catch(e){}
r1 = Number(arg1.toString().replace('.', ''))
r2 = Number(arg2.toString().replace('.', ''))
return (r1 / r2) * Math.pow(10, t2 - t1)
}
/* eslint-enable */
/**
*
*
* @param {*} start_left_timestamp
* @memberof Timeline
*/
/* eslint-disable */
add_graduations(start_left_timestamp) {
let px_per_min = this.canvasW / (this.sec_per_my / 60) // px/min
let px_per_ms = this.canvasW / (this.sec_per_my * 1000) // px/ms
let px_per_sec = this.canvasW / this.sec_per_my // px/ms
let px_per_step = this.graduation_step // px/格 默认最小值20px
let min_per_step = px_per_step / px_per_min // min/格
for (let i = 0; i < this.minutes_per_step.length; i++) {
if (min_per_step <= this.minutes_per_step[i]) { //让每格时间在minutes_per_step规定的范围内
min_per_step = this.minutes_per_step[i]
px_per_step = px_per_min * min_per_step
break
}
}
let medium_step = 30
for (let i = 0; i < this.minutes_per_step.length; i++) {
if (this.distance_between_gtitle / px_per_min <= this.minutes_per_step[i]) {
medium_step = this.minutes_per_step[i]
break
}
}
/*let sec_per_step
for (let i = 0; i < this.minutes_per_step.length; i++) {
if (px_per_sec <= this.minutes_per_step[i]) {
sec_per_step = this.minutes_per_step[i]
px_per_step = sec_per_step
break
}
}*/
let num_steps = this.canvasW / px_per_step //总格数
// let num_steps = this.canvasW / sec_per_step //总格数
// px_per_step = sec_per_step
let graduation_left
let graduation_time
let lineH
let ms_offset = this.ms_to_next_step(start_left_timestamp, min_per_step * 60 * 1000) //开始的偏移时间 ms
let px_offset = ms_offset * px_per_ms //开始的偏移距离 px
let ms_per_step = Math.round(px_per_step / px_per_ms) // ms/step
for (let j = 0; j < num_steps; j++) {
graduation_left = px_offset + j * px_per_step // 距离=开始的偏移距离+格数*px/格
graduation_time = start_left_timestamp + ms_offset + j * ms_per_step //时间=左侧开始时间+偏移时间+格数*ms/格
let date = new Date(graduation_time)
if (date.getUTCHours() === 0 && date.getUTCMinutes() === 0) {
lineH = 25
let big_date = this.graduation_title(date)
this.ctx.fillStyle = `rgba(255,255,255,1)`
if (graduation_left + 37 * 2 < this.canvasW) {
this.ctx.fillText(big_date, graduation_left, 25)
}
} else if (Math.round(graduation_time / (60 * 1000)) % medium_step === 0) {
lineH = 15
let middle_date = this.graduation_title(date)
this.ctx.fillStyle = `rgba(151,158,167,1)`
if (graduation_left + 37 * 2 < this.canvasW) {
this.ctx.fillText(middle_date, graduation_left - 20, 25)
}
} else {
lineH = 10
// this.ctx.fillStyle = `rgba(151,158,167,1)`
// let middle_date = this.graduation_sec(date)
// this.ctx.fillText(middle_date, graduation_left - 20, 30)
}
this.drawLine(graduation_left, 0, graduation_left, lineH, `rgba(151,158,167,1)`, 1)
}
if (this.sec_per_my < 61) {
let sec_step = this.canvasW / this.sec_per_my // 1s 的距离
let sec_per_step
for (let i = 0; i < this.minutes_per_step.length; i++) {
if (sec_step <= this.minutes_per_step[i]) {
sec_per_step = this.minutes_per_step[i]
break
}
}
let num = this.canvasW / sec_per_step
let step_sec_my = sec_per_step / (this.canvasW / this.sec_per_my)// 1步距离的秒数
graduation_left = 0
let sec_time = 0
for (let k = 0; k < num; k++) {
this.drawLine(graduation_left, 0, graduation_left, 10, `rgba(151,158,167,1)`, 1)
if (k % 4 === 0) {
this.ctx.fillStyle = `rgba(151,158,167,1)`
let sec = '0:00:'
if (Math.round(sec_time) > 9) {
sec += Math.round(sec_time)
} else {
sec += '0' + Math.round(sec_time)
}
if (graduation_left + 37 * 2 < this.canvasW) {
this.ctx.fillText(sec, graduation_left, 25)
}
}
graduation_left += sec_per_step
sec_time += step_sec_my
}
}
}
/**
*
*
* @param {*} beginX
* @param {*} beginY
* @param {*} endX
* @param {*} endY
* @param {*} color
* @param {*} width
* @memberof Timeline
*/
drawLine(beginX, beginY, endX, endY, color, width) {
this.ctx.beginPath()
this.ctx.moveTo(beginX, beginY)
this.ctx.lineTo(endX, endY)
this.ctx.strokeStyle = color
this.ctx.lineWidth = width
this.ctx.stroke()
}
add_cells(cells) {
if (Array.isArray(cells)) {
cells.forEach(cell => {
this.draw_cell(cell)
})
}
}
draw_cell(cell) {
let px_per_ms = this.canvasW / (this.sec_per_my * 1000) // px/ms
let beginX = (cell.beginTime - this.start_timestamp) * px_per_ms
let cell_width = (cell.endTime - cell.beginTime) * px_per_ms
this.ctx.fillStyle = cell.style.background
this.ctx.fillRect(beginX, 0, cell_width, 15)
}
add_events() {
if (this.canvas.addEventListener) {
this.canvas.addEventListener('mousewheel', this.mousewheelFunc.bind(this))
this.canvas.addEventListener('mousedown', this.mousedownFunc.bind(this))
this.canvas.addEventListener('mousemove', this.mousemoveFunc.bind(this))
this.canvas.addEventListener('mouseup', this.mouseupFunc.bind(this))
this.canvas.addEventListener('mouseout', this.mouseoutFunc.bind(this))
}
}
/**
*
*
* @param {*} e
* @memberof Timeline
*/
mousedownFunc(e) {
this.g_isMousedown = true
this.g_mousedownCursor = this.get_cursor_x_position(e) //记住mousedown的位置
}
/**
*
*
* @param {*} e
* @memberof Timeline
*/
mousemoveFunc(e) {
let pos_x = this.get_cursor_x_position(e)
let px_per_ms = this.canvasW / (this.sec_per_my * 1000) // px/ms
this.clearCanvas()
if (this.g_isMousedown) {
let diff_x = pos_x - this.g_mousedownCursor
this.start_timestamp = this.start_timestamp - Math.round(diff_x / px_per_ms)
this.init(this.start_timestamp, this.timecell, true)
this.g_isMousemove = true
this.g_mousedownCursor = pos_x
} else {
let time = this.start_timestamp + pos_x / px_per_ms
this.init(this.start_timestamp, this.timecell, true)
this.drawLine(pos_x - 1, 0, pos_x - 1, 25, `rgb(194, 202, 215)`, 1)
this.ctx.fillStyle = `rgb(194, 202, 215)`
this.ctx.fillText(this.changeTime(time), pos_x - 36, 35)
}
}
/**
*
*
* @param {*} e
* @memberof Timeline
*/
mouseupFunc(e) {
if (this.g_isMousemove) { //拖动 事件
this.g_isMousemove = false
this.g_isMousedown = false
this.returnTime = this.start_timestamp + (this.sec_per_my * 1000) / 2
} else { // click 事件
this.g_isMousedown = false
let posx = this.get_cursor_x_position(e) //鼠标距离 px
let ms_per_px = (this.zoom * 3600 * 1000) / this.canvasW // ms/px
this.returnTime = this.start_timestamp + posx * ms_per_px
this.set_time_to_middle(this.returnTime)
}
}
/**
*
*
* @param {*} e
* @memberof Timeline
*/
mouseoutFunc(e) {
this.clearCanvas()
this.init(this.start_timestamp, this.timecell, true)
}
/**
*
*
* @param {*} e
* @returns
* @memberof Timeline
*/
mousewheelFunc(e) {
if (e && e.preventDefault) {
e.preventDefault()
} else {
window.event.returnValue = false
return false
}
let delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)))
let middle_time = this.start_timestamp + (this.sec_per_my * 1000) / 2 //ms 记住当前中间的时间
if (delta < 0) {
this.zoom = this.zoom + 4
if (this.zoom >= 24) {
this.zoom = 24 //放大最大24小时
}
this.hours_per_ruler = this.zoom
} else if (delta > 0) { // 放大
this.zoom = this.zoom - 4
if (this.zoom <= 1) {
this.zoom = 1 //缩小最小1小时
}
this.hours_per_ruler = this.zoom
}
this.clearCanvas()
this.start_timestamp = middle_time - (this.sec_per_my * 1000) / 2 //start_timestamp = 当前中间的时间 - zoom/2
this.init(this.start_timestamp, this.timecell, true)
}
/**
*
*
* @param {*} e
* @returns
* @memberof Timeline
*/
get_cursor_x_position(e) {
let posx = 0
if (!e) {
e = window.event
}
if (e.pageX || e.pageY) {
posx = e.pageX
} else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft
}
return posx
}
/**
*
*
* @param {*} datetime
* @returns datetime
* @memberof Timeline
*/
graduation_title(datetime) {
if (datetime.getHours() === 0 && datetime.getMinutes() === 0 && datetime.getMilliseconds() === 0) {
return ('0' + (datetime.getDate() - 8).toString()).substr(-2) + '.' + ('0' + (datetime.getMonth() + 1).toString()).substr(-2) + '.' + datetime.getFullYear()
}
return datetime.getHours() - 8 + ':' + ('0' + datetime.getMinutes().toString()).substr(-2)
}
graduation_sec(datetime) {
return datetime.getUTCSeconds()
}
/**
*
*
* @param {*} time
* @returns
* @memberof Timeline
*/
changeTime(time) {
let newTime = new Date(time)
let year = newTime.getFullYear()
let month = newTime.getMonth() + 1
if (month < 10) {
let month = '0' + month
}
let date = newTime.getDate()
if (date < 10) {
let date = '0' + date
}
let hour = newTime.getHours()
if (hour < 10) {
hour = '0' + hour
}
let minute = newTime.getMinutes()
if (minute < 10) {
minute = '0' + minute
}
let second = newTime.getSeconds()
if (second < 10) {
second = '0' + second
}
return hour - 8 + ':' + minute + ':' + second
}
/**
*
*
* @param {*} timestamp
* @param {*} step
* @returns
* @memberof Timeline
*/
ms_to_next_step(timestamp, step) {
let remainder = timestamp % step
return remainder ? step - remainder : 0
}
/**
*
*
* @param {*} time
* @memberof Timeline
*/
set_time_to_middle(time) {
this.clearCanvas()
this.start_timestamp = time - (this.sec_per_my * 1000) / 2
this.init(this.start_timestamp, this.timecell, true)
}
/**
*
*
* @param {*} callback
* @memberof Timeline
*/
returnMouseupTime(callback) {
if (this.returnTime) {
if (callback) {
callback.call(this, this.returnTime)
}
}
}
/**
*
*
* @memberof Timeline
*/
clearCanvas() {
this.ctx.clearRect(0, 0, this.params.canvasW, this.params.canvasH)
}
}