HTML Canvas& Javascript - 模拟滚动悬停事件

时间:2017-05-10 16:45:28

标签: javascript html html5 events canvas

我试图在HTML画布中编写一个滚动滚动功能,方法是定义一个hover变量,检测指定悬停区域上的鼠标事件,并(在这样做时)添加或减去此悬停变量取决于悬停在哪个区域。此hover变量连接到一系列选择按钮的位置,出于本示例的目的,这些按钮包含数字0到30.当这一系列选择按钮的任一端悬停在它们上方时,它们全部向上移动或者像滚动一样向下,但为了让它像滚动一样,你必须保持鼠标移动,因为每个新的rendered事件画布只有mousemove

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

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

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

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

&#13;
&#13;
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);
&#13;
canvas {
  z-index: -1;
  margin: 1em auto;
  border: 1px solid black;
  display: block;
  background: #9F3A9B;
}
&#13;
<!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>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分: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>