mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-03 20:34:40 +01:00 
			
		
		
		
	Compare commits
	
		
			32 Commits
		
	
	
		
			v0.15.0
			...
			zsviczian-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b82a0749b1 | ||
| 
						 | 
					a772362599 | ||
| 
						 | 
					3f11ca0a44 | ||
| 
						 | 
					808e4711f9 | ||
| 
						 | 
					a26e8bade8 | ||
| 
						 | 
					fc7135c5d1 | ||
| 
						 | 
					f017a60101 | ||
| 
						 | 
					17f9f64eda | ||
| 
						 | 
					619e4061f5 | ||
| 
						 | 
					028ad1ee81 | ||
| 
						 | 
					09a05a4a1c | ||
| 
						 | 
					c4738b31fb | ||
| 
						 | 
					87e6638e9e | ||
| 
						 | 
					0a6d41ecf9 | ||
| 
						 | 
					11109fcc62 | ||
| 
						 | 
					c7346e3a77 | ||
| 
						 | 
					fd030de669 | ||
| 
						 | 
					f77975cee5 | ||
| 
						 | 
					f994e5d71d | ||
| 
						 | 
					77028f4d08 | ||
| 
						 | 
					fb29bb4816 | ||
| 
						 | 
					23fcddb2a3 | ||
| 
						 | 
					b314b939b2 | ||
| 
						 | 
					bc687fea1b | ||
| 
						 | 
					e15f313fe7 | ||
| 
						 | 
					9f02922c91 | ||
| 
						 | 
					2b6819eb2d | ||
| 
						 | 
					c0e9b8d7bc | ||
| 
						 | 
					c8c683c025 | ||
| 
						 | 
					2117fbbc57 | ||
| 
						 | 
					3b9953f57f | ||
| 
						 | 
					7d1efb7f8b | 
@@ -1361,6 +1361,10 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
      document.querySelector(".excalidraw")!,
 | 
			
		||||
    ).getPropertyValue("--color-selection");
 | 
			
		||||
 | 
			
		||||
    //const now = Date.now();
 | 
			
		||||
    //if (!this.state.shouldCacheIgnoreZoom) {
 | 
			
		||||
    //  console.log(`renderScene`, now);
 | 
			
		||||
    //}
 | 
			
		||||
    renderScene(
 | 
			
		||||
      {
 | 
			
		||||
        elements: renderingElements,
 | 
			
		||||
@@ -1397,11 +1401,14 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
          if (this.state.scrolledOutside !== scrolledOutside) {
 | 
			
		||||
            this.setState({ scrolledOutside });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          this.scheduleImageRefresh();
 | 
			
		||||
          //if (!this.state.shouldCacheIgnoreZoom) {
 | 
			
		||||
          //  setTimeout(() => console.log(`after renderScene`, now));
 | 
			
		||||
          //}
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      THROTTLE_NEXT_RENDER && window.EXCALIDRAW_THROTTLE_RENDER === true,
 | 
			
		||||
      true ||
 | 
			
		||||
        (THROTTLE_NEXT_RENDER && window.EXCALIDRAW_THROTTLE_RENDER === true),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (!THROTTLE_NEXT_RENDER) {
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ import { RenderConfig } from "../scene/types";
 | 
			
		||||
import { distance, getFontString, getFontFamilyString, isRTL } from "../utils";
 | 
			
		||||
import { getCornerRadius, isPathALoop, isRightAngle } from "../math";
 | 
			
		||||
import rough from "roughjs/bin/rough";
 | 
			
		||||
import { AppState, BinaryFiles, Zoom } from "../types";
 | 
			
		||||
import { AppState, BinaryFiles, NormalizedZoomValue, Zoom } from "../types";
 | 
			
		||||
import { getDefaultAppState } from "../appState";
 | 
			
		||||
import {
 | 
			
		||||
  BOUND_TEXT_PADDING,
 | 
			
		||||
@@ -93,6 +93,50 @@ export interface ExcalidrawElementWithCanvas {
 | 
			
		||||
  boundTextElementVersion: number | null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const cappedElementCanvasSize = (
 | 
			
		||||
  element: NonDeletedExcalidrawElement,
 | 
			
		||||
  zoom: Zoom,
 | 
			
		||||
): {
 | 
			
		||||
  width: number;
 | 
			
		||||
  height: number;
 | 
			
		||||
  zoomValue: NormalizedZoomValue;
 | 
			
		||||
} => {
 | 
			
		||||
  const sizelimit = 16777216; // 2^24
 | 
			
		||||
  const padding = getCanvasPadding(element);
 | 
			
		||||
  let zoomValue = zoom.value;
 | 
			
		||||
 | 
			
		||||
  if (isLinearElement(element) || isFreeDrawElement(element)) {
 | 
			
		||||
    const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
 | 
			
		||||
 | 
			
		||||
    let width = distance(x1, x2) * window.devicePixelRatio + padding * 2;
 | 
			
		||||
    let height = distance(y1, y2) * window.devicePixelRatio + padding * 2;
 | 
			
		||||
 | 
			
		||||
    const size = width * height * zoomValue * zoomValue;
 | 
			
		||||
    if (size > sizelimit) {
 | 
			
		||||
      zoomValue = Math.sqrt(
 | 
			
		||||
        sizelimit / (width * height),
 | 
			
		||||
      ) as NormalizedZoomValue;
 | 
			
		||||
      width = distance(x1, x2) * window.devicePixelRatio + padding * 2;
 | 
			
		||||
      height = distance(y1, y2) * window.devicePixelRatio + padding * 2;
 | 
			
		||||
    }
 | 
			
		||||
    width *= zoomValue;
 | 
			
		||||
    height *= zoomValue;
 | 
			
		||||
    return { width, height, zoomValue };
 | 
			
		||||
  }
 | 
			
		||||
  let width = element.width * window.devicePixelRatio + padding * 2;
 | 
			
		||||
  let height = element.height * window.devicePixelRatio + padding * 2;
 | 
			
		||||
 | 
			
		||||
  const size = width * height * zoomValue * zoomValue;
 | 
			
		||||
  if (size > sizelimit) {
 | 
			
		||||
    zoomValue = Math.sqrt(sizelimit / (width * height)) as NormalizedZoomValue;
 | 
			
		||||
    width = element.width * window.devicePixelRatio + padding * 2;
 | 
			
		||||
    height = element.height * window.devicePixelRatio + padding * 2;
 | 
			
		||||
  }
 | 
			
		||||
  width *= zoomValue;
 | 
			
		||||
  height *= zoomValue;
 | 
			
		||||
  return { width, height, zoomValue };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const generateElementCanvas = (
 | 
			
		||||
  element: NonDeletedExcalidrawElement,
 | 
			
		||||
  zoom: Zoom,
 | 
			
		||||
@@ -102,44 +146,35 @@ const generateElementCanvas = (
 | 
			
		||||
  const context = canvas.getContext("2d")!;
 | 
			
		||||
  const padding = getCanvasPadding(element);
 | 
			
		||||
 | 
			
		||||
  const { width, height, zoomValue } = cappedElementCanvasSize(element, zoom);
 | 
			
		||||
 | 
			
		||||
  canvas.width = width;
 | 
			
		||||
  canvas.height = height;
 | 
			
		||||
 | 
			
		||||
  let canvasOffsetX = 0;
 | 
			
		||||
  let canvasOffsetY = 0;
 | 
			
		||||
 | 
			
		||||
  if (isLinearElement(element) || isFreeDrawElement(element)) {
 | 
			
		||||
    const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
 | 
			
		||||
 | 
			
		||||
    canvas.width =
 | 
			
		||||
      distance(x1, x2) * window.devicePixelRatio * zoom.value +
 | 
			
		||||
      padding * zoom.value * 2;
 | 
			
		||||
    canvas.height =
 | 
			
		||||
      distance(y1, y2) * window.devicePixelRatio * zoom.value +
 | 
			
		||||
      padding * zoom.value * 2;
 | 
			
		||||
    const [x1, y1] = getElementAbsoluteCoords(element);
 | 
			
		||||
 | 
			
		||||
    canvasOffsetX =
 | 
			
		||||
      element.x > x1
 | 
			
		||||
        ? distance(element.x, x1) * window.devicePixelRatio * zoom.value
 | 
			
		||||
        ? distance(element.x, x1) * window.devicePixelRatio * zoomValue
 | 
			
		||||
        : 0;
 | 
			
		||||
 | 
			
		||||
    canvasOffsetY =
 | 
			
		||||
      element.y > y1
 | 
			
		||||
        ? distance(element.y, y1) * window.devicePixelRatio * zoom.value
 | 
			
		||||
        ? distance(element.y, y1) * window.devicePixelRatio * zoomValue
 | 
			
		||||
        : 0;
 | 
			
		||||
 | 
			
		||||
    context.translate(canvasOffsetX, canvasOffsetY);
 | 
			
		||||
  } else {
 | 
			
		||||
    canvas.width =
 | 
			
		||||
      element.width * window.devicePixelRatio * zoom.value +
 | 
			
		||||
      padding * zoom.value * 2;
 | 
			
		||||
    canvas.height =
 | 
			
		||||
      element.height * window.devicePixelRatio * zoom.value +
 | 
			
		||||
      padding * zoom.value * 2;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  context.save();
 | 
			
		||||
  context.translate(padding * zoom.value, padding * zoom.value);
 | 
			
		||||
  context.translate(padding * zoomValue, padding * zoomValue);
 | 
			
		||||
  context.scale(
 | 
			
		||||
    window.devicePixelRatio * zoom.value,
 | 
			
		||||
    window.devicePixelRatio * zoom.value,
 | 
			
		||||
    window.devicePixelRatio * zoomValue,
 | 
			
		||||
    window.devicePixelRatio * zoomValue,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const rc = rough.canvas(canvas);
 | 
			
		||||
@@ -156,7 +191,7 @@ const generateElementCanvas = (
 | 
			
		||||
    element,
 | 
			
		||||
    canvas,
 | 
			
		||||
    theme: renderConfig.theme,
 | 
			
		||||
    canvasZoom: zoom.value,
 | 
			
		||||
    canvasZoom: zoomValue,
 | 
			
		||||
    canvasOffsetX,
 | 
			
		||||
    canvasOffsetY,
 | 
			
		||||
    boundTextElementVersion: getBoundTextElement(element)?.version || null,
 | 
			
		||||
@@ -422,6 +457,11 @@ const generateElementShape = (
 | 
			
		||||
  // `null` indicates no rc shape applicable for this element type
 | 
			
		||||
  // (= do not generate anything)
 | 
			
		||||
  if (shape === undefined) {
 | 
			
		||||
    const prevElementWithCanvas = elementWithCanvasCache.get(element);
 | 
			
		||||
    if (prevElementWithCanvas?.canvas) {
 | 
			
		||||
      prevElementWithCanvas.canvas.width = 0;
 | 
			
		||||
      prevElementWithCanvas.canvas.height = 0;
 | 
			
		||||
    }
 | 
			
		||||
    elementWithCanvasCache.delete(element);
 | 
			
		||||
 | 
			
		||||
    switch (element.type) {
 | 
			
		||||
@@ -685,7 +725,10 @@ const generateElementWithCanvas = (
 | 
			
		||||
      zoom,
 | 
			
		||||
      renderConfig,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (prevElementWithCanvas?.canvas) {
 | 
			
		||||
      prevElementWithCanvas.canvas.width = 0;
 | 
			
		||||
      prevElementWithCanvas.canvas.height = 0;
 | 
			
		||||
    }
 | 
			
		||||
    elementWithCanvasCache.set(element, elementWithCanvas);
 | 
			
		||||
 | 
			
		||||
    return elementWithCanvas;
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,11 @@ import {
 | 
			
		||||
} from "../scene/scrollbars";
 | 
			
		||||
import { getSelectedElements } from "../scene/selection";
 | 
			
		||||
 | 
			
		||||
import { renderElement, renderElementToSvg } from "./renderElement";
 | 
			
		||||
import {
 | 
			
		||||
  cappedElementCanvasSize,
 | 
			
		||||
  renderElement,
 | 
			
		||||
  renderElementToSvg,
 | 
			
		||||
} from "./renderElement";
 | 
			
		||||
import { getClientColors } from "../clients";
 | 
			
		||||
import { LinearElementEditor } from "../element/linearElementEditor";
 | 
			
		||||
import {
 | 
			
		||||
@@ -407,6 +411,21 @@ export const _renderScene = ({
 | 
			
		||||
 | 
			
		||||
    let editingLinearElement: NonDeleted<ExcalidrawLinearElement> | undefined =
 | 
			
		||||
      undefined;
 | 
			
		||||
    const start = Date.now();
 | 
			
		||||
    const showDebug = false; //!appState.shouldCacheIgnoreZoom && (appState.zoom.value < 0.5);
 | 
			
		||||
    if (showDebug) {
 | 
			
		||||
      console.log("start: renderElements");
 | 
			
		||||
    }
 | 
			
		||||
    console.log(
 | 
			
		||||
      visibleElements.length,
 | 
			
		||||
      appState.zoom.value,
 | 
			
		||||
      Math.round(
 | 
			
		||||
        visibleElements.reduce((acc, el) => {
 | 
			
		||||
          const { width, height } = cappedElementCanvasSize(el, appState.zoom);
 | 
			
		||||
          return acc + width * height;
 | 
			
		||||
        }, 0),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    visibleElements.forEach((element) => {
 | 
			
		||||
      try {
 | 
			
		||||
        renderElement(element, rc, context, renderConfig, appState);
 | 
			
		||||
@@ -426,7 +445,9 @@ export const _renderScene = ({
 | 
			
		||||
        console.error(error);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (showDebug) {
 | 
			
		||||
      console.log(`finish: renderElements ${Date.now() - start}`);
 | 
			
		||||
    }
 | 
			
		||||
    if (editingLinearElement) {
 | 
			
		||||
      renderLinearPointHandles(
 | 
			
		||||
        context,
 | 
			
		||||
@@ -636,10 +657,8 @@ export const _renderScene = ({
 | 
			
		||||
      }
 | 
			
		||||
      context.restore();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Reset zoom
 | 
			
		||||
    context.restore();
 | 
			
		||||
 | 
			
		||||
    // Paint remote pointers
 | 
			
		||||
    for (const clientId in renderConfig.remotePointerViewportCoords) {
 | 
			
		||||
      let { x, y } = renderConfig.remotePointerViewportCoords[clientId];
 | 
			
		||||
@@ -787,7 +806,6 @@ export const _renderScene = ({
 | 
			
		||||
      });
 | 
			
		||||
      context.restore();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    context.restore();
 | 
			
		||||
    return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
 | 
			
		||||
  };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								src/utils.ts
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/utils.ts
									
									
									
									
									
								
							@@ -135,17 +135,24 @@ export const throttleRAF = <T extends any[]>(
 | 
			
		||||
  let timerId: number | null = null;
 | 
			
		||||
  let lastArgs: T | null = null;
 | 
			
		||||
  let lastArgsTrailing: T | null = null;
 | 
			
		||||
  let watchdog: number | null = null;
 | 
			
		||||
 | 
			
		||||
  const scheduleFunc = (args: T) => {
 | 
			
		||||
    timerId = window.requestAnimationFrame(() => {
 | 
			
		||||
      timerId = null;
 | 
			
		||||
      //console.log("start render in animation frame");
 | 
			
		||||
      fn(...args);
 | 
			
		||||
      //console.log("render done in animation frame");
 | 
			
		||||
      lastArgs = null;
 | 
			
		||||
      if (lastArgsTrailing) {
 | 
			
		||||
        //console.log("last args trailing", lastArgsTrailing);
 | 
			
		||||
        lastArgs = lastArgsTrailing;
 | 
			
		||||
        lastArgsTrailing = null;
 | 
			
		||||
        scheduleFunc(lastArgs);
 | 
			
		||||
      }
 | 
			
		||||
      if (watchdog) {
 | 
			
		||||
        clearTimeout(watchdog);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@@ -165,6 +172,9 @@ export const throttleRAF = <T extends any[]>(
 | 
			
		||||
    if (timerId !== null) {
 | 
			
		||||
      cancelAnimationFrame(timerId);
 | 
			
		||||
      timerId = null;
 | 
			
		||||
      if (watchdog) {
 | 
			
		||||
        clearTimeout(watchdog);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (lastArgs) {
 | 
			
		||||
      fn(...(lastArgsTrailing || lastArgs));
 | 
			
		||||
@@ -176,8 +186,19 @@ export const throttleRAF = <T extends any[]>(
 | 
			
		||||
    if (timerId !== null) {
 | 
			
		||||
      cancelAnimationFrame(timerId);
 | 
			
		||||
      timerId = null;
 | 
			
		||||
      if (watchdog) {
 | 
			
		||||
        clearTimeout(watchdog);
 | 
			
		||||
        watchdog = null;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  watchdog = window.setTimeout(() => {
 | 
			
		||||
    console.log("watchdog", timerId);
 | 
			
		||||
    if (timerId !== null) {
 | 
			
		||||
      cancelAnimationFrame(timerId);
 | 
			
		||||
      timerId = null;
 | 
			
		||||
    }
 | 
			
		||||
  }, 1000);
 | 
			
		||||
  return ret;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user