前面的話
一般地,不同的運動形式會產生不同的軌跡。但僅憑肉眼去識別運動軌跡,其實并不是很直觀。因此,在頁面中顯示運動軌跡,是一個重要的問題。物體初始態時,受到外力大小不同,則初速度不同。如何在網頁中模擬投擲效果,也需要解決。接下來,將詳細介紹軌跡和投擲
運動軌跡
元素在運動過程中,不同的運動形式會產生不同的軌跡。如果不把軌跡表示出來,我們只能通過肉眼來區分運動形式。表示軌跡通常有兩種方式:創建小元素和使用canvas
創建小元素
創建小元素原理上比較簡單,但是性能較差。在元素移動時,創建一個2px*2px的小元素,并添加到頁面上。以最簡單的勻速運動為例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .track{ width: 2px; height: 2px; background-color:#000; position:absolute; } </style> </head> <body> <button id="btn1">開始運動</button> <button id="btn2">刪除軌跡</button> <button id="reset">還原</button> <div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div> <script> function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function move(obj,attr,target,step,fn){ //如果沒有建立定時器對象,則在obj下建立定時器對象 if(!obj.timers){obj.timers = {};} //清除定時器 clearInterval(obj.timers[attr]); //聲明當前值變量cur var cur; //判斷步長step的正負值 step = parseInt(getCSS(obj,attr)) < target ? step : -step; //開啟定時器 obj.timers[attr] = setInterval(function(){ //獲取樣式當前值并賦值給cur cur = parseFloat(getCSS(obj,attr)); ////若步長設置值使得元素超過目標點時,將步長設置值更改為目標點值 - 當前值 if((cur + step - target)*step > 0){ step = target - cur; } //將合適的步長值賦值給元素的樣式 obj.style[attr] = cur + step + 'px'; //設置軌跡 createTracks(obj.offsetLeft,obj.offsetTop) //當元素到達目標點后,停止定時器 if(step == target - cur){ clearInterval(obj.timers[attr]); obj.timers[attr] = 0; fn && fn.call(obj); } },20); } function createTracks(x,y){ var ele = document.createElement('div'); ele.className = 'track'; ele.style.left = x + 'px'; ele.style.top = y + 'px'; document.body.appendChild(ele); } function deleteTracks(){ var eles = document.getElementsByTagName('div'); for(var i = 0 ;i < eles.length; i++){ if(eles[i].className == 'track'){ document.body.removeChild(eles[i]); i--; } } } btn1.onclick = function(){ move(test,'left',300,10) } btn2.onclick = function(){ deleteTracks() } reset.onclick = function(){ history.go(); } </script> </body> </html>
使用canvas
使用canvas也可以實現運動軌跡,且性能較好,只不過需要掌握canvas的一些基礎知識
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn1">開始運動</button> <button id="btn2">刪除軌跡</button> <button id="reset">還原</button> <div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div> <canvas id="drawing" style="position:absolute;left:0;top:0;z-index:-1"></canvas> <script> var context; backupCanvas(); function backupCanvas(){ var drawing = document.getElementById('drawing'); drawing.setAttribute('width',document.documentElement.clientWidth); drawing.setAttribute('height',document.documentElement.clientHeight); if(drawing.getContext){ context = drawing.getContext('2d'); context.beginPath(); context.moveTo(test.offsetLeft,test.offsetTop); } } function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function move(obj,attr,target,step,fn){ //如果沒有建立定時器對象,則在obj下建立定時器對象 if(!obj.timers){obj.timers = {};} //清除定時器 clearInterval(obj.timers[attr]); //聲明當前值變量cur var cur; //判斷步長step的正負值 step = parseInt(getCSS(obj,attr)) < target ? step : -step; //開啟定時器 obj.timers[attr] = setInterval(function(){ //獲取樣式當前值并賦值給cur cur = parseFloat(getCSS(obj,attr)); ////若步長設置值使得元素超過目標點時,將步長設置值更改為目標點值 - 當前值 if((cur + step - target)*step > 0){ step = target - cur; } //將合適的步長值賦值給元素的樣式 obj.style[attr] = cur + step + 'px'; createCanvasTracks(obj.offsetLeft,obj.offsetTop); //當元素到達目標點后,停止定時器 if(step == target - cur){ clearInterval(obj.timers[attr]); obj.timers[attr] = 0; fn && fn.call(obj); } },20); } function createCanvasTracks(x,y){ context.lineTo(x,y); context.stroke(); } function deleteCanvasTracks(){ drawing.height = drawing.height; } btn1.onclick = function(){ move(test,'left',300,10) } btn2.onclick = function(){ deleteCanvasTracks() } reset.onclick = function(){ history.go(); } </script> </body> </html>
拖拽軌跡
物體在拖拽的時候,同樣也存在著拖拽軌跡。由于拖拽的運動形式不固定,因此軌跡也不固定
同樣地,拖拽軌跡也有創建小元素和使用canvas兩種方法
【創建小元素】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .track{ width: 2px; height: 2px; background-color:#000; position:absolute; } </style> </head> <body> <button id="btn">刪除軌跡</button> <button id="reset">還原</button> <div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div> <script> function createTracks(x,y){ var ele = document.createElement('div'); ele.className = 'track'; ele.style.left = x + 'px'; ele.style.top = y + 'px'; document.body.appendChild(ele); } function deleteTracks(){ var eles = document.getElementsByTagName('div'); for(var i = 0 ;i < eles.length; i++){ if(eles[i].className == 'track'){ document.body.removeChild(eles[i]); i--; } } } btn.onclick = function(){ deleteTracks() } reset.onclick = function(){ history.go(); } test.onmousedown = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時鼠標距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //獲取此時鼠標距離視口左上角的x軸及y軸距離 x2 = e.clientX; y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; //創建軌跡 createTracks(X,Y); } document.onmouseup = function(e){ //當鼠標抬起時,拖拽結束,則將onmousemove賦值為null即可 document.onmousemove = null; //釋放全局捕獲 if(test.releaseCapture){ test.releaseCapture(); } } //阻止默認行為 return false; //IE8-瀏覽器阻止默認行為 if(test.setCapture){ test.setCapture(); } } </script> </body> </html>
【使用canvas】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn">刪除軌跡</button> <button id="reset">還原</button> <div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div> <canvas id="drawing" style="position:absolute;left:0;top:0;z-index:-1"></canvas> <script> var context; backupCanvas(); function backupCanvas(){ var drawing = document.getElementById('drawing'); drawing.setAttribute('width',document.documentElement.clientWidth); drawing.setAttribute('height',document.documentElement.clientHeight); if(drawing.getContext){ context = drawing.getContext('2d'); context.beginPath(); context.moveTo(test.offsetLeft,test.offsetTop); } } function createCanvasTracks(x,y){ context.lineTo(x,y); context.stroke(); } function deleteCanvasTracks(){ drawing.height = drawing.height; } btn.onclick = function(){ deleteCanvasTracks() } reset.onclick = function(){ history.go(); } test.onmousedown = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時鼠標距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //獲取此時鼠標距離視口左上角的x軸及y軸距離 x2 = e.clientX; y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; //創建軌跡 createCanvasTracks(X,Y); } document.onmouseup = function(e){ //當鼠標抬起時,拖拽結束,則將onmousemove賦值為null即可 document.onmousemove = null; //釋放全局捕獲 if(test.releaseCapture){ test.releaseCapture(); } } //阻止默認行為 return false; //IE8-瀏覽器阻止默認行為 if(test.setCapture){ test.setCapture(); } } </script> </body> </html>
投擲
如何使用javascript模擬出投擲的效果呢?javascript里面并沒有力的概念。我們可以使用投擲速度為基準,當投擲速度快時,元素的速度也快;投擲速度慢時,元素速度同樣也慢
問題來了,投擲速度如何確定。在javascript中模擬運動通常是使用一定頻率的定時器來實現,投擲速度也同樣如此。速度就相當于是一定時間的位移(或稱為步長)。在定時器頻率確定的情況下同,位移的確定其實就是找起始點和結束點這兩個點的坐標位置
拖拽共涉及到三個鼠標事件:mousedown、mousemove和mouseup。結束點的位置是mouseup事件的鼠標位置(注意:mouseup事件的鼠標位置和最后一次mousemove事件的鼠標位置是相同的),而開始點的位置可以是mousemove事件倒數第二次的鼠標位置。這兩個位置是拖拽運動的最后兩個運動位置,通過確定他們就可以確定投擲步長了
下面以勻速運動為例,來進行實現
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="reset">還原</button> <div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div> <script> function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function move(obj,attr,target,step,fn){ //如果沒有建立定時器對象,則在obj下建立定時器對象 if(!obj.timers){obj.timers = {};} //清除定時器 clearInterval(obj.timers[attr]); //聲明當前值變量cur var cur; //判斷步長step的正負值 step = parseInt(getCSS(obj,attr)) < target ? step : -step; //開啟定時器 obj.timers[attr] = setInterval(function(){ //獲取樣式當前值并賦值給cur cur = parseFloat(getCSS(obj,attr)); //若步長設置值使得元素超過目標點時,將步長設置值更改為目標點值 - 當前值 if((cur + step - target)*step > 0){ step = target - cur; } //將合適的步長值賦值給元素的樣式 obj.style[attr] = cur + step + 'px'; //當元素到達目標點后,停止定時器 if(step == target - cur){ clearInterval(obj.timers[attr]); obj.timers[attr] = 0; fn && fn.call(obj); } },30); } reset.onclick = function(){ history.go(); } test.onmousedown = function(e){ e = e || event; //聲明投擲步長值 var stepX,stepY; //聲明上一次mousemove事件的坐標位置 var lastX2 = e.clientX; var lastY2 = e.clientY; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時鼠標距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //獲取此時鼠標距離視口左上角的x軸及y軸距離 var x2 = e.clientX; var y2 = e.clientY; stepX = x2 - lastX2; stepY = y2 - lastY2; lastX2 = e.clientX; lastY2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ e = e || event; var maxHeight = document.documentElement.clientHeight - test.offsetHeight; var maxWidth = document.documentElement.clientWidth - test.offsetWidth; //以設置的投擲速度來進行勻速運動 //向右投擲 if(stepX > 0){ move(test,'left',maxWidth,stepX); //向左投擲 }else{ move(test,'left',0,stepX); } //向下投擲 if(stepY > 0){ move(test,'top',maxHeight,stepY); //向上投擲 }else{ move(test,'top',0,stepY); } //當鼠標抬起時,拖拽結束,則將onmousemove賦值為null即可 document.onmousemove = null; //釋放全局捕獲 if(test.releaseCapture){ test.releaseCapture(); } } //阻止默認行為 return false; //IE8-瀏覽器阻止默認行為 if(test.setCapture){ test.setCapture(); } } </script> </body> </html>
文章列表