javascript - Stencil js - requestAnimationFrame 不会出现在所有组件中
问题描述
我使用 stencil.js 创建了一个简单的目录项组件。在组件中有画布标签,我在上面绘制了动画曲线。在 componentDidLoad 函数中,我定义了画布,对其进行初始化并调用 animate 函数。这是组件本身的代码:
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>
);
}
}
如您所见,我导入了画布代码,这里是:
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>
);
最终结果是组件显示画布和线条,但动画仅在其中一个中发生。其余的在初始状态下是静态的。请告诉我为什么会发生这种情况以及我可以做些什么来修复它,以便所有组件都可以动画。重要的是要注意,当我刷新代码并且浏览器通过热重载刷新时,另一个组件会在每次刷新时启动动画等等。
谢谢您的帮助。
解决方案
问题是某些变量是在函数外部定义的,initCanvas
因此在所有组件(line1
、line2
和)之间共享。因此,每次您调用它们时,它们都会被覆盖。ctx
canvasElem
initCanvas
一个快速的解决方案是将它包装在一个类中:
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>
元素的引用。
推荐阅读
- php - 文件上传为base64字符串,然后根据mime类型显示
- php - count():参数必须是数组或者实现了Countable的对象,未初始化的字符串偏移量:0
- java - Hibernate 不向数据库插入记录
- vuejs2 - 为什么我的 Vue Spa 不能在移动离线模式下工作?
- c++ - 在 C++ 中创建新对象只有两种方法吗?(使用和不使用 `new` 关键字)
- python - 基于条件使用 Pandas 的不同排序规则
- spring-boot - 使用spring-boot 2在Junit中注入RabbitTemplate的问题
- javascript - 离散轴标签
- javascript - javascript中的XOR运算符与python中的XOR运算符不同
- rx-java - RxJava 将发出的值收集到数组