mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 05:48:59 +08:00
refactor(editor): mount worker renderer in editor host (#10055)
This would allow for easier integration with current test runner, since the two column layout is removed. The `ViewportTurboRender` canvas and its debug UI are only enabled if the extension is added, which won't affect the AFFiNE entry. <img width="945" alt="image" src="https://github.com/user-attachments/assets/dc82daa4-cbed-4eb9-9660-28c3f7d35722" />
This commit is contained in:
@@ -31,7 +31,6 @@ const font = new FontFace(
|
||||
);
|
||||
// @ts-expect-error worker env
|
||||
self.fonts && self.fonts.add(font);
|
||||
font.load().catch(console.error);
|
||||
|
||||
function getBaseline() {
|
||||
const fontSize = 15;
|
||||
@@ -57,9 +56,15 @@ class SectionPainter {
|
||||
this.canvas = new OffscreenCanvas(width, height);
|
||||
this.ctx = this.canvas.getContext('2d')!;
|
||||
this.ctx.scale(dpr, dpr);
|
||||
this.ctx.fillStyle = 'lightgrey';
|
||||
this.ctx.fillRect(0, 0, width, height);
|
||||
this.zoom = zoom;
|
||||
this.clearBackground();
|
||||
}
|
||||
|
||||
private clearBackground() {
|
||||
if (!this.canvas || !this.ctx) return;
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.ctx.fillStyle = 'white';
|
||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
paint(section: SectionLayout) {
|
||||
@@ -70,6 +75,8 @@ class SectionPainter {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearBackground();
|
||||
|
||||
ctx.scale(this.zoom, this.zoom);
|
||||
|
||||
// Track rendered positions to avoid duplicate rendering across all paragraphs and sentences
|
||||
@@ -105,9 +112,23 @@ class SectionPainter {
|
||||
}
|
||||
|
||||
const painter = new SectionPainter();
|
||||
let fontLoaded = false;
|
||||
|
||||
font
|
||||
.load()
|
||||
.then(() => {
|
||||
fontLoaded = true;
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
self.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
||||
const { type, data } = e.data;
|
||||
|
||||
if (!fontLoaded) {
|
||||
await font.load();
|
||||
fontLoaded = true;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'initSection': {
|
||||
const { width, height, dpr, zoom } = data;
|
||||
@@ -115,7 +136,6 @@ self.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
||||
break;
|
||||
}
|
||||
case 'paintSection': {
|
||||
await font.load();
|
||||
const { section } = data;
|
||||
painter.paint(section);
|
||||
break;
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { type Container, type ServiceIdentifier } from '@blocksuite/global/di';
|
||||
import { Pane } from 'tweakpane';
|
||||
|
||||
import { getSentenceRects, segmentSentences } from './text-utils.js';
|
||||
import { type ParagraphLayout, type SectionLayout } from './types.js';
|
||||
@@ -24,6 +25,7 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
private lastZoom: number | null = null;
|
||||
private lastSection: SectionLayout | null = null;
|
||||
private lastBitmap: ImageBitmap | null = null;
|
||||
private debugPane: Pane | null = null;
|
||||
|
||||
constructor(std: BlockStdScope) {
|
||||
super(std);
|
||||
@@ -33,20 +35,57 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
}
|
||||
|
||||
override mounted() {
|
||||
const targetContainer = document.querySelector('#right-column')!;
|
||||
if (!targetContainer.querySelector('canvas')) {
|
||||
targetContainer.append(this.canvas);
|
||||
const viewportElement = document.querySelector('.affine-edgeless-viewport');
|
||||
if (viewportElement) {
|
||||
viewportElement.append(this.canvas);
|
||||
this.debugPane = new Pane({ container: viewportElement as HTMLElement });
|
||||
this.initTweakpane();
|
||||
}
|
||||
this.viewport.viewportUpdated.on(async () => {
|
||||
await this.render();
|
||||
});
|
||||
|
||||
document.fonts.load('15px Inter').then(async () => {
|
||||
await this.render();
|
||||
});
|
||||
}
|
||||
|
||||
override unmounted() {
|
||||
if (this.lastBitmap) {
|
||||
this.lastBitmap.close();
|
||||
}
|
||||
if (this.debugPane) {
|
||||
this.debugPane.dispose();
|
||||
this.debugPane = null;
|
||||
}
|
||||
this.worker.terminate();
|
||||
this.canvas.remove();
|
||||
}
|
||||
|
||||
private initTweakpane() {
|
||||
if (!this.debugPane) return;
|
||||
|
||||
const paneElement = this.debugPane.element;
|
||||
paneElement.style.position = 'absolute';
|
||||
paneElement.style.top = '10px';
|
||||
paneElement.style.left = '10px';
|
||||
paneElement.style.width = '250px';
|
||||
|
||||
this.debugPane.title = 'Viewport Turbo Renderer';
|
||||
|
||||
const params = {
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
this.debugPane
|
||||
.addBinding(params, 'enabled', {
|
||||
label: 'Enable',
|
||||
})
|
||||
.on('change', ({ value }) => {
|
||||
this.canvas.style.display = value ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
get viewport() {
|
||||
return this.std.get(GfxControllerIdentifier).viewport;
|
||||
}
|
||||
@@ -56,6 +95,8 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
}
|
||||
|
||||
getHostLayout() {
|
||||
if (!document.fonts.check('15px Inter')) return null;
|
||||
|
||||
const paragraphBlocks = this.std.host.querySelectorAll(
|
||||
'.affine-paragraph-rich-text-wrapper [data-v-text="true"]'
|
||||
);
|
||||
@@ -169,10 +210,14 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
private syncCanvasSize() {
|
||||
const hostRect = this.getHostRect();
|
||||
const dpr = window.devicePixelRatio;
|
||||
this.canvas.style.width = `${hostRect.width}px`;
|
||||
this.canvas.style.height = `${hostRect.height}px`;
|
||||
this.canvas.style.position = 'absolute';
|
||||
this.canvas.style.left = '0px';
|
||||
this.canvas.style.top = '0px';
|
||||
this.canvas.style.width = '100%';
|
||||
this.canvas.style.height = '100%';
|
||||
this.canvas.width = hostRect.width * dpr;
|
||||
this.canvas.height = hostRect.height * dpr;
|
||||
this.canvas.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
private updateCacheState(section: SectionLayout, bitmapCopy: ImageBitmap) {
|
||||
@@ -225,7 +270,6 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
|
||||
public async render(): Promise<void> {
|
||||
const hostLayout = this.getHostLayout();
|
||||
|
||||
if (!hostLayout) return;
|
||||
|
||||
const { section } = hostLayout;
|
||||
|
||||
Reference in New Issue
Block a user