【问题标题】:HTML Canvas & Javascript - Emulating a Scroll-On-Hover EventHTML Canvas & Javascript - 模拟 Scroll-On-Hover 事件
【发布时间】:2017-05-10 16:45:28
【问题描述】:

我正在尝试通过定义hover 变量、检测指定悬停区域上的鼠标事件以及(在这样做时)添加或减去此悬停变量来在 HTML 画布中编写悬停滚动功能,具体取决于悬停在哪个区域。这个hover 变量连接到一系列选择按钮的位置,就本示例而言,这些按钮包含数字 0 到 30。当这一系列选择按钮的任一端悬停在上方时,它们都会向上或向下移动就像滚动一样,但要使它像滚动一样,您必须保持鼠标移动,因为在每个新的 mousemove 事件上,画布仅是 rendered

我的问题是如何触发mouseover 上的事件,以便if (lowerHoverBoxHitTest(x, y))(upperHoverBoxHitTest(x, y))(即,如果鼠标悬停在下面脚本中定义的任一命中框上)悬停变量一直 被添加到设置的增量 (0.1) 直到鼠标离开该区域。我已经尝试用while 循环替换function mouseMove 中的if/else 语句(因为这在逻辑上类似于我所要求的)所以

while (lowerHoverBoxHitTest(x, y)) {
    if (hover < 750) {
      hover-=0.1;
    }
} 

while (upperHoverBoxHitTest(x, y)) {
    if (hover > 0) {
      hover+=0.1;
    }
}

但这只会导致页面崩溃(可能会触发无限循环?)。除了 this 之外,Stack Overflow 上没有太多关于此的内容,但如果您的画布中有很多其他不想滚动的东西,则此解决方案没有用(除非您要绝对定义它们的位置,我不想)我在我的完整项目中做的。任何帮助将不胜感激。

var c=document.getElementById('game'),
		canvasX=c.offsetLeft,
		canvasY=c.offsetTop,
		ctx=c.getContext('2d');

var hover=0;

function upperHoverBoxHitTest(x, y) {
	return (x >= 0) && (x <= 350) && (y >= 0) && (y <= 50);
}

function lowerHoverBoxHitTest(x, y) {
	return (x >= 0) && (x <= 350) && (y >= 450) && (y <= 500);
}

var selectionForMenu = function(id, text, y) {
	this.id = id;
	this.text = text;
	this.y = y;
}

selectionForMenu.prototype.makeSelection = function() {
	ctx.beginPath();
	ctx.fillStyle='#A84FA5';
	ctx.fillRect(0, this.y+hover, 350, 30)
	ctx.stroke();

	ctx.font='10px Noto Sans';
	ctx.fillStyle='white';
	ctx.textAlign='left';
	ctx.fillText(this.text, 10, this.y+hover+19);
}

var Paint = function(element) {
	this.element = element;
	this.shapes = [];
}

Paint.prototype.addShape = function(shape) {
	this.shapes.push(shape);
}

Paint.prototype.render = function() {
	ctx.clearRect(0, 0, this.element.width, this.element.height);

  for (var i=0; i<this.shapes.length; i++) {
		this.shapes[i].makeSelection();
	}
}

var paint = new Paint(c);
for (i=0; i<30; i++) {
	paint.addShape(new selectionForMenu(i+1, i, i*30));
}

paint.render();

function mouseMove(event) {
	var x = event.x - canvasX;
	var y = event.y - canvasY;
  paint.render();

	if (lowerHoverBoxHitTest(x, y)) {
    hover+=1;
  } else if (upperHoverBoxHitTest(x, y)) {
    hover-=1;
  }
}

c.addEventListener('mousemove', mouseMove);
canvas {
  z-index: -1;
  margin: 1em auto;
  border: 1px solid black;
  display: block;
  background: #9F3A9B;
}
<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>uTalk Demo</title>
	<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'>
</head>
<body>
	<canvas id="game" width = "350" height = "500"></canvas>
</body>
</html>

【问题讨论】:

    标签: javascript html events canvas


    【解决方案1】:

    通过动画循环制作动画。

    您需要有一个动画循环,如果满足条件,它将增加/减少值。如果你有一个循环(这比为每个动画对象添加一个动画循环更好)或作为它自己的函数,这个循环可以是另一个循环的一部分。

    动画循环执行所有渲染,并且仅在需要时进行(无需渲染已渲染的内容)。

    演示

    Demo 是 OP 代码的副本,其中修改了滚动动画并给用户一些反馈。虽然作为一个滚动选择框不完整,但它需要一些调整才能有用。

    var c = document.getElementById('game'),
    canvasX = c.offsetLeft,
    canvasY = c.offsetTop,
    ctx = c.getContext('2d');
    
    var hover = 0;
    const overTypes = {
        lower : 1,
        raise : 2,
        none : 0,
    }
    var overBox = 0;
    var overDist = 0;
    const maxSpeed = 4;
    const shapeSize = 30;
    const hoverScrollSize = 50;
    const gradUp = ctx.createLinearGradient(0, 0, 0, hoverScrollSize);
    const gradDown = ctx.createLinearGradient(0, ctx.canvas.height - hoverScrollSize, 0, ctx.canvas.height);
    gradUp.addColorStop(0, `rgba(${0xA8},${0x4F},${0xB5},1)`);
    gradUp.addColorStop(1, `rgba(${0xA8},${0x4F},${0xB5},0)`);
    gradDown.addColorStop(1, `rgba(${0xB8},${0x5F},${0xB5},1)`);
    gradDown.addColorStop(0, `rgba(${0xB8},${0x5F},${0xB5},0)`);
    
    c.addEventListener('mousemove', mouseMove)
    c.addEventListener('mouseout', () => {
        overBox = overTypes.none
    }); // stop scroll when mouse out of canvas
    // start the first frame
    requestAnimationFrame(() => {
        paint.render(); // paint first frame
        requestAnimationFrame(mainLoop); // start main loop
    });
    function mainLoop() {
        if (overBox !== overTypes.none) {
            hover += overDist / hoverScrollSize * (overBox === overTypes.lower ? maxSpeed : -maxSpeed);
            var bottom =  - (paint.shapes.length - ctx.canvas.height / shapeSize) * shapeSize;
    
            hover = hover > 0 ? 0 : hover < bottom ? bottom : hover;
            paint.render();
        }
        requestAnimationFrame(mainLoop); // wait for next animation frame
    }
    
    function mouseMove(event) {
        var x = event.clientX - canvasX;
        var y = event.clientY - canvasY;
        if (lowerHoverBoxHitTest(x, y)) {
            overBox = overTypes.lower;
        } else if (upperHoverBoxHitTest(x, y)) {
            overBox = overTypes.raise;
        } else {
            overBox = overTypes.none;
        }
    }
    
    function upperHoverBoxHitTest(x, y) {
        overDist = hoverScrollSize - y;
        return (x >= 0) && (x <= 350) && (y >= 0) && (y <= hoverScrollSize);
    }
    
    function lowerHoverBoxHitTest(x, y) {
        overDist = y - (ctx.canvas.height - hoverScrollSize);
        return (x >= 0) && (x <= 350) && (y >= ctx.canvas.height - hoverScrollSize) && (y <= ctx.canvas.height);
    }
    
    var selectionForMenu = function (id, text, y) {
        this.id = id;
        this.text = text;
        this.y = y;
    }
    
    selectionForMenu.prototype.makeSelection = function () {
        ctx.beginPath();
        ctx.fillStyle = '#A84FA5';
        ctx.fillRect(0, this.y + hover, 350, shapeSize)
        ctx.stroke();
    
        ctx.font = '10px Noto Sans';
        ctx.fillStyle = 'white';
        ctx.textAlign = 'left';
        ctx.fillText(this.text, 10, this.y + hover + 19);
    }
    
    var Paint = function (element) {
        this.element = element;
        this.shapes = [];
    }
    
    Paint.prototype.addShape = function (shape) {
        this.shapes.push(shape);
    }
    
    Paint.prototype.render = function () {
        ctx.clearRect(0, 0, this.element.width, this.element.height);
    
        for (var i = 0; i < this.shapes.length; i++) {
            this.shapes[i].makeSelection();
        }
        if (overBox !== overTypes.none) {
            ctx.globalAlpha = 0.4 * (overDist / 50);
            ctx.globalCompositeOperation = "lighter";
            if (overBox === overTypes.raise) {
                ctx.fillStyle = gradUp;
                ctx.fillRect(0, 0, ctx.canvas.width, hoverScrollSize);
            } else if (overBox === overTypes.lower) {
                ctx.fillStyle = gradDown;
                ctx.fillRect(0, ctx.canvas.height - hoverScrollSize, ctx.canvas.width, hoverScrollSize);
            }
            ctx.globalCompositeOperation = "source-over";
            ctx.globalAlpha = 1;
        }
    }
    
    var paint = new Paint(c);
    for (i = 0; i < 30; i++) {
        paint.addShape(new selectionForMenu(i + 1, i, i * 30));
    }
    
    paint.render();
    canvas {
      z-index: -1;
      margin: 1em auto;
      border: 1px solid black;
      display: block;
      background: #9F3A9B;
    }
    <!doctype html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>uTalk Demo</title>
    	<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'>
    </head>
    <body>
    	<canvas id="game" width = "350" height = "150"></canvas>
    </body>
    </html>

    【讨论】:

    • 谢谢,这正是我想要的。我几乎让它工作了,但由于某种原因,悬停区域仅限于my expanded canvas 的屏幕顶部。澄清一下,我想在菜单的顶部和底部有两个小区域(350x50px),它们使用您实现的overDist 速度梯度触发相应的降低或升高。但是,目前这两个框似乎彼此相邻,或者更确切地说,lowerHoverBox 没有做任何事情。
    • @JonathanConnell 只需更改坐标即可。我只是压缩列表,使其适合 sn-p 窗口,理想情况下,您可以将大小作为变量,以便您可以将其设为任意大小。
    • 我的问题是较高的hoverBox 似乎会触发lowerraise 事件,具体取决于您在其中的位置,而较低的框会触发非常快速的滚动。我试过只更改 hitTest 函数中的坐标,但似乎没有用。
    • @JonathanConnell 我已经更新了代码以使用画布的大小来设置下部滚动区域。如果您滚动页面以到达底部滚动区域,则问题出在鼠标上。我已将它(鼠标事件)更改为使用客户端坐标
    • 如果你不介意看一下,我会被困在一个悬赏here 的相关问题上。
    猜你喜欢
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2023-03-21
    • 1970-01-01
    相关资源
    最近更新 更多