Stencil js - requestAnimationFrame 不会出现在所有组件中
Stencil js - requestAnimationFrame does not occure in all components
我使用 stencil.js 创建了一个简单的目录项组件。在组件中有 canvas 标签,我在上面绘制了动画曲线。
在 componentDidLoad 函数中,我定义了 canvas,对其进行初始化并调用动画函数。这是组件本身的代码:
import { Component, Host, h, Prop, Element } from "@stencil/core";
import { Item } from "../hotb-catalog-container/data";
import { initCanvas, init, animate } from "./wave.js";
import axios from "axios";
@Component({
tag: "hotb-catalog-item",
styleUrl: "hotb-catalog-item.scss",
shadow: false,
})
export class HotbCatalogItem {
@Prop() item: Item;
@Element() el: HTMLElement;
// @Event({ bubbles: true, composed: true }) itemSelect: EventEmitter<Item>;
itemSelected(event: Event) {
event.preventDefault();
//sessionStorage.setItem("item", JSON.stringify(this.item));
axios.post("/item", { item: this.item }).then(() => {
window.location.href = "http://localhost:3000/item";
});
}
componentDidLoad() {
let canvas = this.el.querySelector('canvas');
initCanvas(canvas);
init();
animate();
}
render() {
return (
<Host>
<div class="heading">
<h1>{this.item.name}</h1>
<span> מ{this.item.origin} · {this.item.taste}</span>
</div>
<div class="center-part">
<div class="center-part__img"></div>
<div class="center-part__icon center-part__icon--temp">
{this.item.temp}°C
</div>
<div class="center-part__icon center-part__icon--time">
{this.item.time}
</div>
</div>
<a
href="/item"
onClick={this.itemSelected.bind(this)}
class="primary-btn homepage__tea"
>
לצפיה
</a>
<canvas></canvas>
</Host>
);
}
}
如你所见,我导入了canvas代码,这里是:
let line1;
let line2;
var ctx;
var canvasElem;
function initCanvas(canvas) {
canvasElem = canvas;
ctx = canvas.getContext("2d");
const parent = canvas.parentElement;
canvas.width = parent.clientWidth;
canvas.height = parent.scrollHeight;
}
class LineGen {
constructor(x, y, directionY, cpX, directionCPX, cpY, directionCPY, size, color) {
this.x = x;
this.y = y;
this.directionY = directionY;
this.cpX = cpX;
this.directionCPX = directionCPX;
this.cpY = cpY;
this.directionCPY = directionCPY;
this.size = size;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.quadraticCurveTo(this.cpX, this.cpY, 400, this.y);
ctx.strokeStyle = this.color;
ctx.lineWidth = this.size;
ctx.stroke();
}
update() {
if (this.y > 490 || this.y < 400) {
this.directionY = -this.directionY;
}
if (this.color === '#E07D31') {
if (this.cpX > 390 || this.cpX < 10) {
this.directionCPX = -this.directionCPX;
}
if (this.cpY > 590 || this.cpY < 150) {
this.directionCPY = -this.directionCPY;
}
} else if (this.color === '#49E048') {
if (this.cpX > 390 || this.cpX < 10) {
this.directionCPX = -this.directionCPX;
}
if (this.cpY > 560 || this.cpY < 240) {
this.directionCPY = -this.directionCPY;
}
}
//this.y += this.directionY;
this.cpX += this.directionCPX;
this.cpY += this.directionCPY;
this.draw();
}
}
class Line extends LineGen {
constructor(x, y, directionY, cpX, directionCPX, cpY, directionCPY, size, color) {
super(x, y, directionY, cpX, directionCPX, cpY, directionCPY, size, color);
}
}
function init() {
let x = 0;
let y = 400;
let y2 = 380;
let directionY = -.2;
let cpX = 100;
let cpX2 = 100;
let directionCPX = .15;
let cpY = 300;
let cpY2 = 300;
let directionCPY = .15;
let directionCPY2 = .178125;
let size = 2;
let color = '#E07D31';
let color2 = '#49E048';
line1 = new Line(x, y, directionY, cpX, directionCPX, cpY, directionCPY2, size, color);
line2 = new Line(x, y2, directionY, cpX, directionCPX, cpY, directionCPY, size, color2);
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvasElem.width, canvasElem.height);
line1.update();
line2.update();
}
export { initCanvas, init, animate };
现在,目录项组件位于容器组件内部,如下所示:
let items;
if (this.newItemsArr.length == 0) {
items = <div class="no-items">אין משקאות שתואמים לחיפוש שלך</div>;
} else {
items = this.newItemsArr.map((item) => (
<hotb-catalog-item item={item}></hotb-catalog-item>
));
}
return (
<Host>
{items}
</Host>
);
最终结果是组件显示 canvas 和线条,但动画仅在其中一个中出现。其余的在它们的初始状态下是静态的。请告诉我为什么会发生这种情况以及我可以做些什么来修复它以便所有组件都可以动画。重要的是要注意,当我刷新代码并且浏览器通过热重载刷新时,另一个组件开始动画等等每次刷新。
感谢您的帮助。
问题是某些变量是在 initCanvas
函数之外定义的,因此在所有组件之间共享(line1
、line2
、ctx
和 canvasElem
).所以每次你调用 initCanvas
它们都会被覆盖。
一个快速的解决方案是将其包装在 class:
export class WaveCanvas {
constructor(canvas) {
this.canvasElem = canvas;
this.ctx = canvas.getContext("2d");
const parent = canvas.parentElement;
canvas.width = parent.clientWidth;
canvas.height = parent.scrollHeight;
}
init() {
// ...
this.line1 = new Line(...);
}
animate() {
requestAnimationFrame(() => this.animate());
this.ctx.clearRect(0, 0, this.canvasElem.width, this.canvasElem.height);
this.line1.update();
this.line2.update();
}
}
然后在组件中实例化:
componentDidLoad() {
let canvas = this.el.querySelector('canvas');
const waveCanvas = new WaveCanvas(canvas);
waveCanvas.init();
waveCanvas.animate();
}
这样 WaveCanvas
的每个实例都将保存自己对正确 <canvas>
元素的引用。
我使用 stencil.js 创建了一个简单的目录项组件。在组件中有 canvas 标签,我在上面绘制了动画曲线。 在 componentDidLoad 函数中,我定义了 canvas,对其进行初始化并调用动画函数。这是组件本身的代码:
import { Component, Host, h, Prop, Element } from "@stencil/core";
import { Item } from "../hotb-catalog-container/data";
import { initCanvas, init, animate } from "./wave.js";
import axios from "axios";
@Component({
tag: "hotb-catalog-item",
styleUrl: "hotb-catalog-item.scss",
shadow: false,
})
export class HotbCatalogItem {
@Prop() item: Item;
@Element() el: HTMLElement;
// @Event({ bubbles: true, composed: true }) itemSelect: EventEmitter<Item>;
itemSelected(event: Event) {
event.preventDefault();
//sessionStorage.setItem("item", JSON.stringify(this.item));
axios.post("/item", { item: this.item }).then(() => {
window.location.href = "http://localhost:3000/item";
});
}
componentDidLoad() {
let canvas = this.el.querySelector('canvas');
initCanvas(canvas);
init();
animate();
}
render() {
return (
<Host>
<div class="heading">
<h1>{this.item.name}</h1>
<span> מ{this.item.origin} · {this.item.taste}</span>
</div>
<div class="center-part">
<div class="center-part__img"></div>
<div class="center-part__icon center-part__icon--temp">
{this.item.temp}°C
</div>
<div class="center-part__icon center-part__icon--time">
{this.item.time}
</div>
</div>
<a
href="/item"
onClick={this.itemSelected.bind(this)}
class="primary-btn homepage__tea"
>
לצפיה
</a>
<canvas></canvas>
</Host>
);
}
}
如你所见,我导入了canvas代码,这里是:
let line1;
let line2;
var ctx;
var canvasElem;
function initCanvas(canvas) {
canvasElem = canvas;
ctx = canvas.getContext("2d");
const parent = canvas.parentElement;
canvas.width = parent.clientWidth;
canvas.height = parent.scrollHeight;
}
class LineGen {
constructor(x, y, directionY, cpX, directionCPX, cpY, directionCPY, size, color) {
this.x = x;
this.y = y;
this.directionY = directionY;
this.cpX = cpX;
this.directionCPX = directionCPX;
this.cpY = cpY;
this.directionCPY = directionCPY;
this.size = size;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.quadraticCurveTo(this.cpX, this.cpY, 400, this.y);
ctx.strokeStyle = this.color;
ctx.lineWidth = this.size;
ctx.stroke();
}
update() {
if (this.y > 490 || this.y < 400) {
this.directionY = -this.directionY;
}
if (this.color === '#E07D31') {
if (this.cpX > 390 || this.cpX < 10) {
this.directionCPX = -this.directionCPX;
}
if (this.cpY > 590 || this.cpY < 150) {
this.directionCPY = -this.directionCPY;
}
} else if (this.color === '#49E048') {
if (this.cpX > 390 || this.cpX < 10) {
this.directionCPX = -this.directionCPX;
}
if (this.cpY > 560 || this.cpY < 240) {
this.directionCPY = -this.directionCPY;
}
}
//this.y += this.directionY;
this.cpX += this.directionCPX;
this.cpY += this.directionCPY;
this.draw();
}
}
class Line extends LineGen {
constructor(x, y, directionY, cpX, directionCPX, cpY, directionCPY, size, color) {
super(x, y, directionY, cpX, directionCPX, cpY, directionCPY, size, color);
}
}
function init() {
let x = 0;
let y = 400;
let y2 = 380;
let directionY = -.2;
let cpX = 100;
let cpX2 = 100;
let directionCPX = .15;
let cpY = 300;
let cpY2 = 300;
let directionCPY = .15;
let directionCPY2 = .178125;
let size = 2;
let color = '#E07D31';
let color2 = '#49E048';
line1 = new Line(x, y, directionY, cpX, directionCPX, cpY, directionCPY2, size, color);
line2 = new Line(x, y2, directionY, cpX, directionCPX, cpY, directionCPY, size, color2);
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvasElem.width, canvasElem.height);
line1.update();
line2.update();
}
export { initCanvas, init, animate };
现在,目录项组件位于容器组件内部,如下所示:
let items;
if (this.newItemsArr.length == 0) {
items = <div class="no-items">אין משקאות שתואמים לחיפוש שלך</div>;
} else {
items = this.newItemsArr.map((item) => (
<hotb-catalog-item item={item}></hotb-catalog-item>
));
}
return (
<Host>
{items}
</Host>
);
最终结果是组件显示 canvas 和线条,但动画仅在其中一个中出现。其余的在它们的初始状态下是静态的。请告诉我为什么会发生这种情况以及我可以做些什么来修复它以便所有组件都可以动画。重要的是要注意,当我刷新代码并且浏览器通过热重载刷新时,另一个组件开始动画等等每次刷新。
感谢您的帮助。
问题是某些变量是在 initCanvas
函数之外定义的,因此在所有组件之间共享(line1
、line2
、ctx
和 canvasElem
).所以每次你调用 initCanvas
它们都会被覆盖。
一个快速的解决方案是将其包装在 class:
export class WaveCanvas {
constructor(canvas) {
this.canvasElem = canvas;
this.ctx = canvas.getContext("2d");
const parent = canvas.parentElement;
canvas.width = parent.clientWidth;
canvas.height = parent.scrollHeight;
}
init() {
// ...
this.line1 = new Line(...);
}
animate() {
requestAnimationFrame(() => this.animate());
this.ctx.clearRect(0, 0, this.canvasElem.width, this.canvasElem.height);
this.line1.update();
this.line2.update();
}
}
然后在组件中实例化:
componentDidLoad() {
let canvas = this.el.querySelector('canvas');
const waveCanvas = new WaveCanvas(canvas);
waveCanvas.init();
waveCanvas.animate();
}
这样 WaveCanvas
的每个实例都将保存自己对正确 <canvas>
元素的引用。