手把手教你實(shí)現(xiàn)一個(gè)JavaScript時(shí)間軸組件
這是開頭
本文給大家?guī)硪粋€(gè)時(shí)間軸的組件開發(fā)教程,話不多說,先看動圖:

主要功能就是可以拖動時(shí)間軸來定位當(dāng)前時(shí)間,可以通過鼠標(biāo)滾輪來修改當(dāng)前時(shí)間分辨率,也支持顯示時(shí)間段功能,動圖未體現(xiàn),可看下面的本次demo效果示例:

如果對canvas不太熟悉的話可以先看一下教程:https://www.runoob.com/tags/ref-canvas.html
接下來進(jìn)入開發(fā)時(shí)間。
開發(fā)時(shí)間
前端框架依舊使用的是vue,這個(gè)組件交互是通過canvas實(shí)現(xiàn)的,模板非常簡單:
<template>
<div class="timeLineContainer" ref="timeLineContainer">
<canvas
ref="canvas"
@mousemove="onMousemove"
@mouseout="onMouseout"
@mousedown="onMousedown"
@mousewheel="onMouseweel"
></canvas>
</div>
</template>綁定了四個(gè)事件,后續(xù)再細(xì)說。
準(zhǔn)備工作
首先要做的是設(shè)置一下畫布的寬高及獲取畫圖上下文:
{
methods: {
init () {
// 獲取外層寬高
let {
width,
height
} = this.$refs.timeLineContainer.getBoundingClientRect()
this.width = width
this.height = height
// 設(shè)置畫布寬高為外層元素寬高
this.$refs.canvas.width = width
this.$refs.canvas.height = height
// 獲取畫圖上下文
this.ctx = this.$refs.canvas.getContext('2d')
}
}
}中間的白色豎線
中間的白色豎線代表的就是當(dāng)前的時(shí)間,但是就線而言它只是一條線,所以先把它畫了:
{
// 這個(gè)函數(shù)是整個(gè)繪制方法,所有的繪制方法都在此調(diào)用
draw () {
this.drawMiddleLine()
},
// 畫中間的白色豎線
drawMiddleLine () {
let lineWidth = 2
// 線的x坐標(biāo)是時(shí)間軸的中點(diǎn),y坐標(biāo)即時(shí)間軸的高度
let x = this.width / 2
this.drawLine(x, 0, x, this.height, lineWidth, '#fff')
},
// 畫線段方法
drawLine (x1, y1, x2, y2, lineWidth = 1, color = '#fff') {
// 開始一段新路徑
this.ctx.beginPath()
// 設(shè)置線段顏色
this.ctx.strokeStyle = '#fff'
// 設(shè)置線段寬度
this.ctx.lineWidth = lineWidth
// 將路徑起點(diǎn)移到x1,y1
this.ctx.moveTo(x1, y1)
// 將路徑移動到x2,y2
this.ctx.lineTo(x2, y2)
// 把路徑畫出來
this.ctx.stroke()
}
}這樣白色豎線就有了:

時(shí)間刻度
時(shí)間刻度是本組件的核心,支持調(diào)整時(shí)間分辨率(就是整個(gè)時(shí)間軸所表示的時(shí)間范圍,也即每兩刻度之間的一格代表的時(shí)間大?。?,暫定包含0.5, 1, 2, 6, 12, 24這五種,單位是小時(shí),先定義幾個(gè)變量:
// 一小時(shí)的毫秒數(shù)
const ONE_HOUR_STAMP = 60 * 60 * 1000
// 時(shí)間分辨率
const ZOOM = [0.5, 1, 2, 6, 12, 24]
export default {
data () {
return {
// 當(dāng)前所在時(shí)間分辨率的類型索引
currentZoomIndex: 5,
// 當(dāng)前時(shí)間
currentTime: 0,
// 時(shí)間軸左側(cè)起點(diǎn)所代表的時(shí)間,默認(rèn)為當(dāng)天的0點(diǎn)減12小時(shí),即昨天中午12點(diǎn)
startTimestamp:
new Date(moment().format('YYYY-MM-DD 00:00:00')).getTime() -
12 * ONE_HOUR_STAMP,
}
}
}時(shí)間分辨率放在ZOOM的數(shù)組里,先以24分辨率來開發(fā),24代表的是整個(gè)時(shí)間軸表示的時(shí)間范圍為24小時(shí)。但是具體用一格表示多久呢,可以1個(gè)小時(shí)1格,也可以半個(gè)小時(shí)一格,隨便你,這里就用一格表示半個(gè)小時(shí),其他分辨率也是如此,為了方便也把它們裝到一個(gè)數(shù)組里:
// 時(shí)間分辨率對應(yīng)的每格小時(shí)數(shù) const ZOOM_HOUR_GRID = [1 / 60, 1 / 60, 2 / 60, 1 / 6, 0.25, 0.5]
0.5就代表1小格代表0.5個(gè)小時(shí),既然每格代表的小時(shí)數(shù)知道了,那么時(shí)間軸一共需要畫多少格也就確定了:
// 一共可以繪制的格數(shù),時(shí)間軸的時(shí)間范圍小時(shí)數(shù)除以每格代表的小時(shí)數(shù),24/0.5=48 let gridNum = ZOOM[this.currentZoomIndex] / ZOOM_HOUR_GRID[this.currentZoomIndex]
因?yàn)闀r(shí)間計(jì)算都是通過毫秒進(jìn)行計(jì)算,所以先算一下一格代表多少毫秒:
// 一格多少毫秒,將每格代表的小時(shí)數(shù)轉(zhuǎn)成毫秒數(shù)就可以了 let msPerGrid = ZOOM_HOUR_GRID[this.currentZoomIndex] * ONE_HOUR_STAMP
接下來是關(guān)鍵,因?yàn)橐媹D,最終還是要知道像素大小,那么每格是多少像素呢:
// 每格寬度,時(shí)間軸的寬度除以總格數(shù) let pxPerGrid = this.width / gridNum
接下來事情似乎就簡單了,循環(huán)一下畫出刻度就好了:
for (let i = 0; i < gridNum; i++) {
// 橫坐標(biāo)就是當(dāng)前索引乘每格寬度
let x = i * pxPerGrid
// 當(dāng)前刻度的時(shí)間,時(shí)間軸起始時(shí)間加上當(dāng)前格子數(shù)乘每格代表的毫秒數(shù)
let graduationTime = this.startTimestamp + i * msPerGrid
// 刻度高度為時(shí)間軸高度的20%
let h = this.height * 0.2
// 刻度線顏色
this.ctx.fillStyle = 'rgba(151,158,167,1)'
// 顯示時(shí)間
this.ctx.fillText(
this.graduationTitle(graduationTime),
x - 13,// 向左平移一半
h + 15// 加上行高
)
this.drawLine(x, 0, x, h, 1, 'rgba(151,158,167,1)')
}
graduationTitle方法是用來格式時(shí)間的,在0點(diǎn)時(shí)顯示日期而不是時(shí)間:
graduationTitle (datetime) {
let time = moment(datetime)
// 0點(diǎn)則顯示當(dāng)天日期
if (
time.hours() === 0 &&
time.minutes() === 0 &&
time.milliseconds() === 0
) {
return time.format('MM-DD')
} else {// 否則顯示小時(shí)和分鐘
return time.format('HH:mm')
}
}看下效果:

似乎很完美,但是這樣的真的可以了嗎?不妨把起始時(shí)間加上個(gè)15分鐘看一下:
startTimestamp:
new Date(moment().format('YYYY-MM-DD 00:00:00')).getTime() -
12 * ONE_HOUR_STAMP +
15 * 60 * 1000// 加15分鐘
起始時(shí)間加上15分鐘,則為12:15分,看下效果:

可以看到,雖然每格代表的還是半個(gè)小時(shí),但是我們的要求應(yīng)該是逢整點(diǎn)和半點(diǎn)才顯示刻度的,所以起始點(diǎn)應(yīng)該是處在12:00和12:30分兩根刻度的中間才對,所以畫刻度的位置就需要加上一個(gè)偏移量,這里的偏移量很明顯就是12:30-12:15=15分鐘,如果起始點(diǎn)是12:40,那么偏移量就是13:00-12:40=20分鐘,那么怎么算呢?我們不妨把時(shí)間拖回到0點(diǎn),從0開始也許更容易看出來:
比如間距為10,起始點(diǎn)為5,那么與0的偏移量當(dāng)然是5,可以通過5-0也可以通過5%10來算出來,那如果起始點(diǎn)是14,與前一個(gè)點(diǎn)的偏移量是14-10=4,但問題是你不知道前一個(gè)點(diǎn)是多少,所以減不了,只能14%10=4來算。但是我們實(shí)際需要的是與后一個(gè)點(diǎn)的偏移量,很簡單,間距減去它就可以了。

其實(shí)很多差距的計(jì)算都可以通過取余來算,所以:
// 時(shí)間偏移量,初始時(shí)間除每格時(shí)間取余數(shù),
let msOffset = msPerGrid - (this.startTimestamp % msPerGrid)
// 距離偏移量,時(shí)間偏移量和每格時(shí)間比例乘每格像素
let pxOffset = (msOffset / msPerGrid) * pxPerGrid
for (let i = 0; i < gridNum; i++) {
let x = pxOffset + i * pxPerGrid
let graduationTime = this.startTimestamp + msOffset + i * msPerGrid
//...
}效果如下:

但是這樣每個(gè)刻度都顯示時(shí)間沒必要也有點(diǎn)丑,所以可以循環(huán)的時(shí)候加個(gè)判斷條件來選擇性的繪制,因?yàn)槊糠N分辨率也有不同的判斷條件,所以也用一個(gè)數(shù)組來表示:
// 時(shí)間分辨率對應(yīng)的時(shí)間顯示判斷條件
const ZOOM_DATE_SHOW_RULE = [
() => {// 全都顯示
return true
},
date => {// 每五分鐘顯示
return date.getMinutes() % 5 === 0
},
date => {// 顯示10、20、30...分鐘數(shù)
return date.getMinutes() % 10 === 0
},
date => {// 顯示整點(diǎn)和半點(diǎn)小時(shí)
return date.getMinutes() === 0 || date.getMinutes() === 30
},
date => {// 顯示整點(diǎn)小時(shí)
return date.getMinutes() === 0
},
date => {// 顯示2、4、6...整點(diǎn)小時(shí)
return date.getHours() % 2 === 0 && date.getMinutes() === 0
}
]
for (let i = 0; i < gridNum; i++) {
let x = pxOffset + i * pxPerGrid
let graduationTime = this.startTimestamp + msOffset + i * msPerGrid
let h = 0
let date = new Date(graduationTime)
// 0點(diǎn)顯示日期
if (date.getHours() === 0 && date.getMinutes() === 0) {
h = this.height * 0.3
this.ctx.fillStyle = 'rgba(151,158,167,1)'
this.ctx.fillText(
this.graduationTitle(graduationTime),
x - 13,
h + 15
)
} else if (ZOOM_DATE_SHOW_RULE[this.currentZoomIndex](date)) {// 其他根據(jù)判斷條件來顯示
h = this.height * 0.2
this.ctx.fillStyle = 'rgba(151,158,167,1)'
this.ctx.fillText(
this.graduationTitle(graduationTime),
x - 13,
h + 15
)
} else {// 其他不顯示時(shí)間
h = this.height * 0.15
}
this.drawLine(x, 0, x, h, 1, 'rgba(151,158,167,1)')
}效果如下:

鼠標(biāo)移動時(shí)顯示所在時(shí)間
鼠標(biāo)在時(shí)間軸上滑動時(shí)需要實(shí)時(shí)顯示鼠標(biāo)所在位置的時(shí)間,效果如下:

實(shí)現(xiàn)方式就是獲取到鼠標(biāo)相對畫布的位置,然后換算成距起始點(diǎn)的時(shí)間:
{
// 最開始就綁定的鼠標(biāo)移動事件
onMousemove (e) {
// 計(jì)算出相對畫布的位置
let { left } = this.$refs.canvas.getBoundingClientRect()
let x = e.clientX - left
// 計(jì)算出時(shí)間軸上每毫秒多少像素
const PX_PER_MS =
this.width / (ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP) // px/ms
// 計(jì)算所在位置的時(shí)間
let time = this.startTimestamp + x / PX_PER_MS
// 清除畫布
this.clearCanvas(this.width, this.height)
// 繪制
this.draw()
// 繪制實(shí)時(shí)的豎線及時(shí)間
this.drawLine(x, 0, x, this.height * 0.3, 'rgb(194, 202, 215)', 1)
this.ctx.fillStyle = 'rgb(194, 202, 215)'
this.ctx.fillText(
moment(time).format('YYYY-MM-DD HH:mm:ss'),
x - 20,
this.height * 0.3 + 20
)
}
}拖動時(shí)間軸
萬眾矚目的焦點(diǎn)來了,時(shí)間軸時(shí)間軸,當(dāng)然得需要能拖動,不然那叫時(shí)間段,從效果上看好像是鼠標(biāo)拖著時(shí)間軸在滑動,但是實(shí)際上并沒有,跟動畫類似,就是不斷的刷新重繪,因?yàn)槿搜鄣臅捍嫘?yīng),看起來就像在滑動一樣,而時(shí)間軸渲染的依據(jù)就是起始時(shí)間點(diǎn),所以本質(zhì)上就是計(jì)算鼠標(biāo)拖動過程中的起始時(shí)間點(diǎn)是多少,先看一下鼠標(biāo)按下的事件處理函數(shù):
onMousedown (e) {
let { left } = this.$refs.canvas.getBoundingClientRect()
// 也是計(jì)算鼠標(biāo)相當(dāng)于時(shí)間軸左側(cè)的距離
this.mousedownX = e.clientX - left
// 設(shè)置一下標(biāo)志位
this.mousedown = true
// 緩存一下鼠標(biāo)按下時(shí)的起始時(shí)間點(diǎn)
this.mousedownCacheStartTimestamp = this.startTimestamp
}鼠標(biāo)開始移動就又到了鼠標(biāo)移動事件處理的那個(gè)函數(shù):
onMousemove (e) {
let { left } = this.$refs.canvas.getBoundingClientRect()
let x = e.clientX - left
const PX_PER_MS =
this.width / (ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP) // px/ms
if (this.mousedown) {
// 計(jì)算鼠標(biāo)當(dāng)前相當(dāng)于鼠標(biāo)按下那個(gè)點(diǎn)的距離
let diffX = x - this.mousedownX
// 用鼠標(biāo)按下時(shí)的起始時(shí)間點(diǎn)減去拖動過程中的偏移量,往左拖是負(fù)值,減減得正,時(shí)間就是在增加,往右拖時(shí)間就是在減少
this.startTimestamp =
this.mousedownCacheStartTimestamp - Math.round(diffX / PX_PER_MS)
// 不斷刷新重繪就ok了
this.clearCanvas(this.width, this.height)
this.draw()
} else {
// 鼠標(biāo)滑動顯示時(shí)間的邏輯
}
調(diào)整時(shí)間分辨率
調(diào)整時(shí)間分辨率說白了就是調(diào)整時(shí)間軸所表示的時(shí)間范圍,我們的范圍是定義在ZOOM數(shù)組里的,所以通過鼠標(biāo)滾動來調(diào)整之前定義的變量currentZoomIndex,然后重新渲染畫布即可,需要注意的是時(shí)間范圍調(diào)整了,而時(shí)間起始點(diǎn)不變的話那么當(dāng)前時(shí)間就會變,但是我們一般是希望當(dāng)前時(shí)間是不變的,所以需要調(diào)整時(shí)要計(jì)算新的時(shí)間起始點(diǎn):
onMouseweel (event) {
let e = window.event || event
let delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail))
if (delta < 0) {
// 縮小
if (this.currentZoomIndex + 1 >= ZOOM.length - 1) {
this.currentZoomIndex = ZOOM.length - 1
} else {
this.currentZoomIndex++
}
} else if (delta > 0) {
// 放大
if (this.currentZoomIndex - 1 <= 0) {
this.currentZoomIndex = 0
} else {
this.currentZoomIndex--
}
}
this.clearCanvas(this.width, this.height)
// 重新計(jì)算起始時(shí)間點(diǎn),當(dāng)前時(shí)間-新的時(shí)間范圍的一半
this.startTimestamp =
this.currentTime - (ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP) / 2
this.draw()
}繪制時(shí)間段
時(shí)間段就是在時(shí)間軸里帶顏色的矩形塊,先看看時(shí)間段的數(shù)據(jù)結(jié)構(gòu):
[
{
beginTime: new Date('2020-06-10 09:30:00').getTime(),
endTime: new Date('2020-06-10 11:20:00').getTime(),
style: {
background: '#5881CF'
}
}
]接下來就是想辦法把起始時(shí)間用給定的顏色在時(shí)間軸里畫出來,首先要判斷一下時(shí)間段是否在當(dāng)前時(shí)間軸的范圍內(nèi),如果不相交當(dāng)然就不用畫了:
if (item.beginTime <= this.startTimestamp + ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP && item.endTime >= this.startTimestamp) {
// 繪制范圍內(nèi)的線段
}繪制矩形用的是fillRect方法,它的四個(gè)參數(shù)分別是:x、y、width、height,先算起始點(diǎn)的坐標(biāo),y和height可以直接根據(jù)時(shí)間軸的高度來定,所以主要計(jì)算的就是x和width,x是起點(diǎn)值,可以用beginTime-startTimestamp再換算成像素就可以了,需要注意的是可能beginTime小于startTimestamp,負(fù)值顯然是不行的,所以轉(zhuǎn)成0,width就是起始時(shí)間的差值換算成像素,也需要注意小于0的情況,完整代碼如下:
drawTimeSegments () {
const PX_PER_MS =
this.width / (ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP) // px/ms
this.timeSegments.forEach(item => {
if (
item.beginTime <=
this.startTimestamp +
ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP &&
item.endTime >= this.startTimestamp
) {
let x = (item.beginTime - this.startTimestamp) * PX_PER_MS
let w
if (x < 0) {
x = 0
w = (item.endTime - this.startTimestamp) * PX_PER_MS
} else {
w = (item.endTime - item.beginTime) * PX_PER_MS
}
this.ctx.fillStyle = item.style.background
this.ctx.fillRect(x, this.height * 0.6, w, this.height * 0.3)
}
})
}為什么endTime大于時(shí)間軸最大時(shí)間的情況不用特殊處理呢,因?yàn)椴还芤矝]關(guān)系,反正長度都已經(jīng)超出范圍了,再長一點(diǎn)短一點(diǎn)也看不到。

當(dāng)然,這樣單純的顯示一下時(shí)間段意義并不大,一般使用場景是代表在當(dāng)前時(shí)間段內(nèi)才有視頻,所以可以在時(shí)間段存在的情況下對拖動時(shí)間做一下處理,如果拖動到的時(shí)間點(diǎn)不在任何一個(gè)時(shí)間段內(nèi),那么就讓它吸附到離它最近的一個(gè)時(shí)間段的時(shí)間點(diǎn)上。
多個(gè)時(shí)間軸
一個(gè)時(shí)間軸往往是不夠用的,比如同時(shí)要進(jìn)行多路視頻回放,每個(gè)視頻都有自己的時(shí)間段,那么就需要多個(gè)時(shí)間軸來進(jìn)行顯示,這也很簡單,我們把時(shí)間段相關(guān)的代碼抽到一個(gè)單獨(dú)的組件里,然后把內(nèi)部狀態(tài)都通過props進(jìn)行傳遞,這樣就可以進(jìn)行復(fù)用了:
<template>
<div class="timeLineContainer" ref="timeLineContainer">
<canvas ref="canvas"></canvas>
<!--多個(gè)時(shí)間軸-->
<div class="windowList" ref="windowList" v-if="showWindowList && windowList && windowList.length > 1" @scroll="onWindowListScroll">
<WindowListItem
v-for="(item, index) in windowListInner"
ref="WindowListItem"
:key="index"
:index="index"
:data="item"
:totalMS="totalMS"
:startTimestamp="startTimestamp"
:width="width"
:active="item.active"
@click_window_timeSegments="triggerClickWindowTimeSegments"
@click="toggleActive(index)"
></WindowListItem>
</div>
</div>
</template>
每個(gè)單獨(dú)的時(shí)間軸也是一個(gè)canvas:
// WindowListItem.vue
<template>
<div class="windowListItem" :class="{active: active}" ref="windowListItem" @click="onClick">
<span class="order">{{ index + 1 }}</span>
<canvas class="windowListItemCanvas" ref="canvas"></canvas>
</div>
</template>效果如下:

顯示自定義元素
除了時(shí)間段,有時(shí)候我們會想在時(shí)間段上顯示自定義元素,比如在某個(gè)時(shí)間點(diǎn)顯示一張圖片,因?yàn)闀r(shí)間軸是在動的,所以圖片也得跟著動,這可以給使用者提供一個(gè)監(jiān)聽時(shí)間的功能,具體實(shí)現(xiàn)就是在上文的繪制方法draw里實(shí)時(shí)獲取某個(gè)時(shí)間點(diǎn)的位置,然后拋出一個(gè)事件給使用者監(jiān)聽,使用者可以根據(jù)監(jiān)聽到的left、top值來定位元素。
獲取某個(gè)時(shí)間點(diǎn)的位置也很簡單,先判斷這個(gè)時(shí)間點(diǎn)是否在當(dāng)前顯示的范圍內(nèi),不在的話那顯然就不用顯示,在的話再換算成在當(dāng)前時(shí)間軸上的位置,最后加上時(shí)間軸距離頁面的位置即可:
draw () {
// ...
// 更新觀察的時(shí)間位置
this.updateWatchTime()
}// 更新觀察的時(shí)間位置
updateWatchTime () {
this.watchTimeList.forEach((item) => {
// 當(dāng)前不在顯示范圍內(nèi)
if (item.time < this.startTimestamp || item.time > this.startTimestamp + this.totalMS) {
item.callback(-1, -1)
} else { // 在范圍內(nèi)
let x = (item.time - this.startTimestamp) * (this.width / this.totalMS)
let y = 0
let { left, top } = this.$refs.canvas.getBoundingClientRect()
if (item.windowTimeLineIndex !== -1 && this.windowList.length > 1 && item.windowTimeLineIndex >= 0 && item.windowTimeLineIndex < this.windowList.length) {
let rect = this.$refs.WindowListItem[item.windowTimeLineIndex].getRect()
y = rect ? rect.top : top
} else {
y = top
}
item.callback(x + left, y)
}
})
}使用的時(shí)候,通過給你要顯示的元素設(shè)置絕對定位或固定定位,然后監(jiān)聽時(shí)間的當(dāng)前位置來修改元素的位置,如果時(shí)間軸本身的位置都變化了,比如頁面滾動了,需要手動調(diào)用updateWatchTime方法來修正。
效果如下:

總結(jié)
本文介紹了如何實(shí)現(xiàn)一個(gè)視頻時(shí)間軸組件,可以滿足一些常見的場景,筆者也開發(fā)了一個(gè)可以直接使用的組件,文檔:https://github.com/wanglin2/VideoTimeLine,如何不滿足需求,也可以在此組件基礎(chǔ)上進(jìn)行定制。
以上就是手把手教你實(shí)現(xiàn)一個(gè)JavaScript時(shí)間軸組件的詳細(xì)內(nèi)容,更多關(guān)于JavaScript時(shí)間軸組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
dispatchEvent解決重疊元素響應(yīng)事件示例詳解
這篇文章主要為大家介紹了dispatchEvent解決重疊元素響應(yīng)事件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
基于JS正則表達(dá)式實(shí)現(xiàn)模板數(shù)據(jù)動態(tài)渲染(實(shí)現(xiàn)思路詳解)
這篇文章主要介紹了基于JS正則表達(dá)式實(shí)現(xiàn)模板數(shù)據(jù)動態(tài)渲染 ,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
JavaScript實(shí)現(xiàn)彈出DIV層同時(shí)頁面背景漸變成半透明效果
這篇文章主要介紹了JavaScript實(shí)現(xiàn)彈出DIV層同時(shí)頁面背景漸變成半透明效果,涉及JavaScript彈出窗口的實(shí)現(xiàn)及頁面元素屬性動態(tài)變換的相關(guān)技巧,需要的朋友可以參考下2016-03-03
javascript replace()正則替換實(shí)現(xiàn)代碼
javascript-replace()基礎(chǔ),一次完成將"<,>"替換"<>"實(shí)例2010-02-02
JS+CSS實(shí)現(xiàn)分類動態(tài)選擇及移動功能效果代碼
這篇文章主要介紹了JS+CSS實(shí)現(xiàn)分類動態(tài)選擇及移動功能效果代碼,涉及JavaScript實(shí)現(xiàn)頁面元素動態(tài)變換效果實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
JavaScript 面向?qū)ο蠡A(chǔ)簡單示例
這篇文章主要介紹了JavaScript 面向?qū)ο蠡A(chǔ),結(jié)合簡單實(shí)例形式分析了JavaScript面向?qū)ο蟪绦蛟O(shè)計(jì)中類的定義、類方法與屬性相關(guān)操作技巧,需要的朋友可以參考下2019-10-10

