HTML Canvas & Javascript - Emulating a Scroll-On-Hover Event

柔情痞子 提交于 2019-12-25 16:59:14

问题


I am trying to write a scroll-on-hover function in an HTML canvas by defining a hover variable, detecting mouse events over the designated hover area and (on doing so) adding or subtracting to this hover variable depending on which area is hovered over. This hover variable is connected to the position of a series of selection buttons which, for the purposes of this example, contain the numbers 0 to 30. When either end of this series of selection buttons is hovered over they all move up or down as if scrolled, but to make it act like a scroll you must keep the mouse moving as the canvas is only rendered on each new mousemove event.

My question is how can I trigger the event on mouseover such that if (lowerHoverBoxHitTest(x, y)) or (upperHoverBoxHitTest(x, y)) (i.e if the mouse is hovered over either of the hit boxes defined in the script below) the hover variable keeps being added to by the set increment (0.1) until the mouse leaves that area. I have tried replacing the if/else statement in the function mouseMove with a while loop (as it would seem this is logically akin to what I am asking) as so

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

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

but this just causes the page to crash (presumably it triggers an infinite loop?). There isn't much on Stack Overflow about this besides this but this solution is not useful if you have a lot of other things in your canvas that you don't want to scroll (unless you were to define their position absolutely which I don't want to) which I do in my full project. Any help will be appreciated.

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>

回答1:


Animation via animation loops.

You need to have an animation loop that will increment/decrement the value if the conditions are met. This loop can be part of another if you have one (which is better than adding an animation loop for each animated object) or as its own function.

The animation loop does all the rendering, and only if needed (no point rendering something that is already rendered).

Demo

Demo is a copy of the OP's code with modifications to animate the scrolling and give a little user feed back. Though not complete as a scrolling selection box, it will need some tweaking to be useful.

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>


来源:https://stackoverflow.com/questions/43898388/html-canvas-javascript-emulating-a-scroll-on-hover-event

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!