Javascript 在边界内平滑地在画布周围平移

Javascript 在边界内平滑地在画布周围平移,javascript,canvas,html5-canvas,pan,Javascript,Canvas,Html5 Canvas,Pan,我发现的大多数其他解决方案都是以图像为边界,通过css工作,我无法让它们与画布一起工作 我想通过定义画布的任意百分比大小(例如2x-大小的两倍)来创建边界,该大小从画布中心展开,然后让画布视图(红色)在边界(绿色)内移动/平移,鼠标悬停在画布内-它从画布中心移动到边界的所有角落 就像平移方向上的这种行为一样 另外,我想 使用响应画布大小而不是固定画布大小的解决方案 使用诸如CubicInOut放松等放松方式使平移平滑 要检测鼠标悬停在绘制的圆内。可能必须将坐标从屏幕更改为世界坐标才能使其正

我发现的大多数其他解决方案都是以图像为边界,通过css工作,我无法让它们与画布一起工作

我想通过定义画布的任意百分比大小(例如2x-大小的两倍)来创建边界,该大小从画布中心展开,然后让画布视图(红色)在边界(绿色)内移动/平移,鼠标悬停在画布内-它从画布中心移动到边界的所有角落

就像平移方向上的这种行为一样

另外,我想

  • 使用响应画布大小而不是固定画布大小的解决方案
  • 使用诸如CubicInOut放松等放松方式使平移平滑
  • 要检测鼠标悬停在绘制的圆内。可能必须将坐标从屏幕更改为世界坐标才能使其正常工作。但也许他们的方法更简单

公式

这是我试图找出的公式,让它工作!我将只关注鼠标从左到右的一个轴(x轴),但相同的公式应适用于y轴

首先,我得到鼠标沿宽度的“百分比x”。如果“percentX”为0,则平移的“newX”将是左侧的值,否则如果为1,则将是BoundsOrder的右侧(绿色)。因此,“百分比X”决定画布视图将延伸/移动到边界的哪一侧

唯一的问题是这不起作用。所以我的公式一定是错的。由于我的画布平移,圆圈内的鼠标事件也不准确。把所有东西结合起来是很难搞清楚的

代码

<html>
<head>
    <meta charset="UTF-8">
    <title>Panning</title>

    <style type="text/css">
* {
    margin: 0;
    padding: 0;
}

body {
    background: #eee;
    padding: 20px 0;
}

.canvas-container {
    height: 400px;
    width: 100%;
    margin-bottom: 40px;
    border-radius: 10px;
    background: #fff;
    overflow: hidden;
}

canvas {
    width: 100%;
    height: inherit;
}

.container {
    width: 60%;
    margin: 0 auto;
}
    </style>

</head>
<body>

    <!-- CANVAS -->
    <div class="container">
        <div class="canvas-container">
            <canvas id="canvas"></canvas>
        </div>
    </div>

    <!-- SCRIPT -->
    <script type="text/javascript">

        var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width = canvas.clientWidth;
var height = canvas.height = canvas.clientHeight;
var panX = 0, panY = 0;
var scaleBorderFactor = 2; /* scale factor for the bounds */
var mouse = {
    x: 0,
    y: 0
}

function Circle(x, y, radius, color){
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.draw = function(ctx){
        ctx.beginPath();
        ctx.arc(this.x,this.y,this.radius,0,Math.PI*2,false);
        ctx.fillStyle = this.color;
        ctx.fill();
        ctx.closePath();
    }
}

/* Rect Stroke Borders for visual reference */
function RectBorder(x, y, w, h, c){
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;
    this.color = c;
    this.draw = function(ctx){
        ctx.strokeStyle = this.color;
        ctx.lineWidth = 2;
        ctx.strokeRect(this.x, this.y, this.width, this.height);
    };
    /* Draw rect from a center point */
    this.drawAtCenter = function(ctx){
        ctx.strokeStyle = this.color;
        ctx.lineWidth = 2;
        ctx.strokeRect(this.x, this.y, this.width, this.height);
    };
    this.toString = function(){
        console.log("x: "+this.x+", y: "+this.y+", w: "+this.width+", h: "+this.height+", color = "+this.color);
    }
}

function getMousePos(canvas, event) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: (event.clientX - rect.left),
        y: (event.clientY - rect.top)
    };
}

function insideCircle(circle, mouse){
    var dist = Math.sqrt(Math.pow(circle.x - mouse.x,2) + Math.pow(circle.y - mouse.y,2));
    console.log(circle.radius+", d = "+dist)
    return dist <= circle.radius;
}

function lerp(start, end, percent){
  return start*(1 - percent) + percent*end;
}

/* t: current time, b: beginning value, c: change in value, d: duration */
function easeInOutCubic(t, b, c, d) {
    if ((t/=d/2) < 1) return c/2*t*t*t + b;
    return c/2*((t-=2)*t*t + 2) + b;
}

var canvasBorder = new RectBorder(0,0,canvas.width,canvas.height,'red');

var boundsBorder = new RectBorder(-(canvas.width*scaleBorderFactor - canvas.width)/2,-(canvas.height*scaleBorderFactor - canvas.height)/2,canvas.width*scaleBorderFactor,canvas.height*scaleBorderFactor,'green');

var circle = new Circle(200,200,40,'blue');

/* Draw Update */
update();

function update(){
    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    width = canvas.width = canvas.clientWidth;
    height = canvas.height = canvas.clientHeight;

    ctx.clearRect(0,0,width,height);
    ctx.save();

    /* MOUSE */
    percentX = (mouse.x / width);
    percentY = (mouse.y / height);

    /* the 2 sides of boundsBorder */
    var leftSide = (width*scaleBorderFactor - width)/2 - width;
    var rightSide = (width*scaleBorderFactor - width)/2 + width;

    var topSide = (height*scaleBorderFactor - height)/2 - width;
    var bottomSide = (height*scaleBorderFactor - height)/2 + height;


    newX = rightSide * percentX + leftSide * (1 - percentX);
    newY = bottomSide * percentY + topSide * (1 - percentY);


    /* maybe use easeInOutCubic for better smoothness */
    panX = lerp(-newX, mouse.x, 0.1);
    panY = lerp(-newY, mouse.y, 0.1);

    if (insideCircle(circle, mouse)){
        circle.color = "pink";
    } else {
        circle.color = "blue";
    }

    ctx.translate(panX,panY);

    /* Draw both borders only for reference */
    canvasBorder.draw(ctx);
    boundsBorder.drawAtCenter(ctx);

    /* Draw Circle */
    circle.draw(ctx);

    ctx.restore();

    requestAnimationFrame(update);
}

/* Events */
function mousemove(e) {
    mouse = getMousePos(canvas, e);
}

/* Event Listeners */
canvas.addEventListener('mousemove', mousemove);

    </script>
</body>
</html> 
var canvasBorder = new RectBorder(0,0,canvas.width,canvas.height,'red');

/* widthGap is the new leftSide formula.
*  it is the gap differance between border and canvas
*/
var widthGap = (canvas.width*scaleBorderFactor - canvas.width)/2;
var heightGap = (canvas.height*scaleBorderFactor - canvas.height)/2;

var boundsBorder = new RectBorder(-widthGap,-heightGap,canvas.width+widthGap*2,canvas.height+heightGap*2,'green');

var circle = new Circle(200,200,40,'blue');

/* Draw Update */
update();

var newPercentX = 0, newPercentY = 0;
var percentX = 0, percentY = 0;

function update(){
    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    width = canvas.width = canvas.clientWidth;
    height = canvas.height = canvas.clientHeight;

    ctx.clearRect(0,0,width,height);
    ctx.save();

    newPercentX = (mouse.x / width);
    newPercentY = (mouse.y / height);

    /* MOUSE */
    percentX = lerp(percentX,newPercentX,0.05);
    percentY = lerp(percentY,newPercentY,0.05);

    panX = (widthGap) * percentX + (-widthGap) * (1 - percentX);
    panY = (heightGap) * percentY + (-heightGap) * (1 - percentY);

    ctx.translate(-panX,-panY);

    if (insideCircle(circle, mouse)){
        circle.color = "pink";
    } else {
        circle.color = "blue";
    }

    /* Draw both borders only for reference */
    canvasBorder.draw(ctx);
    boundsBorder.drawAtCenter(ctx);

    /* Draw Circle */
    circle.draw(ctx);

    ctx.restore();

    requestAnimationFrame(update);
}