文章出處

前面的話

  一般地,不同的運動形式會產生不同的軌跡。但僅憑肉眼去識別運動軌跡,其實并不是很直觀。因此,在頁面中顯示運動軌跡,是一個重要的問題。物體初始態時,受到外力大小不同,則初速度不同。如何在網頁中模擬投擲效果,也需要解決。接下來,將詳細介紹軌跡和投擲

 

運動軌跡

  元素在運動過程中,不同的運動形式會產生不同的軌跡。如果不把軌跡表示出來,我們只能通過肉眼來區分運動形式。表示軌跡通常有兩種方式:創建小元素和使用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>


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()