@ -12,7 +12,6 @@ import {
SCROLLBAR_COLOR ,
SCROLLBAR_WIDTH ,
} from "../scene/scrollbars" ;
import { getZoomTranslation } from "../scene/zoom" ;
import { getSelectedElements } from "../scene/selection" ;
import { renderElement , renderElementToSvg } from "./renderElement" ;
@ -28,6 +27,7 @@ export function renderScene(
elements : readonly ExcalidrawElement [ ] ,
appState : AppState ,
selectionElement : ExcalidrawElement | null ,
scale : number ,
rc : RoughCanvas ,
canvas : HTMLCanvasElement ,
sceneState : SceneState ,
@ -51,46 +51,11 @@ export function renderScene(
const context = canvas . getContext ( "2d" ) ! ;
// Get initial scale transform as reference for later usage
const initialContextTransform = context . getTransform ( ) ;
// When doing calculations based on canvas width we should used normalized one
const normalizedCanvasWidth =
canvas . width / getContextTransformScaleX ( initialContextTransform ) ;
const normalizedCanvasHeight =
canvas . height / getContextTransformScaleY ( initialContextTransform ) ;
const zoomTranslation = getZoomTranslation ( canvas , sceneState . zoom ) ;
function applyZoom ( context : CanvasRenderingContext2D ) : void {
context . save ( ) ;
// Handle zoom scaling
context . setTransform (
getContextTransformScaleX ( initialContextTransform ) * sceneState . zoom ,
0 ,
0 ,
getContextTransformScaleY ( initialContextTransform ) * sceneState . zoom ,
getContextTransformTranslateX ( context . getTransform ( ) ) ,
getContextTransformTranslateY ( context . getTransform ( ) ) ,
) ;
// Handle zoom translation
context . setTransform (
getContextTransformScaleX ( context . getTransform ( ) ) ,
0 ,
0 ,
getContextTransformScaleY ( context . getTransform ( ) ) ,
getContextTransformTranslateX ( initialContextTransform ) -
zoomTranslation . x ,
getContextTransformTranslateY ( initialContextTransform ) -
zoomTranslation . y ,
) ;
}
function resetZoom ( context : CanvasRenderingContext2D ) : void {
context . restore ( ) ;
}
const normalizedCanvasWidth = canvas . width / scale ;
const normalizedCanvasHeight = canvas . height / scale ;
// Paint background
context . save ( ) ;
if ( typeof sceneState . viewBackgroundColor === "string" ) {
const hasTransparence =
sceneState . viewBackgroundColor === "transparent" ||
@ -99,12 +64,20 @@ export function renderScene(
if ( hasTransparence ) {
context . clearRect ( 0 , 0 , normalizedCanvasWidth , normalizedCanvasHeight ) ;
}
const fillStyle = context . fillStyle ;
context . fillStyle = sceneState . viewBackgroundColor ;
context . fillRect ( 0 , 0 , normalizedCanvasWidth , normalizedCanvasHeight ) ;
context . fillStyle = fillStyle ;
} else {
context . clearRect ( 0 , 0 , normalizedCanvasWidth , normalizedCanvasHeight ) ;
}
context . restore ( ) ;
// Apply zoom
const zoomTranslationX = ( - normalizedCanvasWidth * ( sceneState . zoom - 1 ) ) / 2 ;
const zoomTranslationY =
( - normalizedCanvasHeight * ( sceneState . zoom - 1 ) ) / 2 ;
context . translate ( zoomTranslationX , zoomTranslationY ) ;
context . scale ( sceneState . zoom , sceneState . zoom ) ;
// Paint visible elements
const visibleElements = elements . filter ( element = >
@ -116,15 +89,12 @@ export function renderScene(
) ,
) ;
applyZoom ( context ) ;
visibleElements . forEach ( element = > {
renderElement ( element , rc , context , renderOptimizations , sceneState ) ;
} ) ;
resetZoom ( context ) ;
// Pain selection element
if ( selectionElement ) {
applyZoom ( context ) ;
renderElement (
selectionElement ,
rc ,
@ -132,7 +102,6 @@ export function renderScene(
renderOptimizations ,
sceneState ,
) ;
resetZoom ( context ) ;
}
// Pain selected elements
@ -140,7 +109,6 @@ export function renderScene(
const selectedElements = getSelectedElements ( elements , appState ) ;
const dashledLinePadding = 4 / sceneState . zoom ;
applyZoom ( context ) ;
context . translate ( sceneState . scrollX , sceneState . scrollY ) ;
selectedElements . forEach ( element = > {
const [
@ -166,11 +134,10 @@ export function renderScene(
context . lineWidth = lineWidth ;
context . setLineDash ( initialLineDash ) ;
} ) ;
resetZoom( context ) ;
context. translate ( - sceneState . scrollX , - sceneState . scrollY ) ;
// Paint resize handlers
if ( selectedElements . length === 1 && selectedElements [ 0 ] . type !== "text" ) {
applyZoom ( context ) ;
context . translate ( sceneState . scrollX , sceneState . scrollY ) ;
context . fillStyle = "#fff" ;
const handlers = handlerRectangles ( selectedElements [ 0 ] , sceneState . zoom ) ;
@ -183,10 +150,14 @@ export function renderScene(
context . strokeRect ( handler [ 0 ] , handler [ 1 ] , handler [ 2 ] , handler [ 3 ] ) ;
context . lineWidth = lineWidth ;
} ) ;
resetZoom( context ) ;
context. translate ( - sceneState . scrollX , - sceneState . scrollY ) ;
}
}
// Reset zoom
context . scale ( 1 / sceneState . zoom , 1 / sceneState . zoom ) ;
context . translate ( - zoomTranslationX , - zoomTranslationY ) ;
// Paint remote pointers
for ( const clientId in sceneState . remotePointerViewportCoords ) {
let { x , y } = sceneState . remotePointerViewportCoords [ clientId ] ;
@ -237,7 +208,8 @@ export function renderScene(
sceneState ,
) ;
context . save ( ) ;
const fillStyle = context . fillStyle ;
const strokeStyle = context . strokeStyle ;
context . fillStyle = SCROLLBAR_COLOR ;
context . strokeStyle = "rgba(255,255,255,0.8)" ;
[ scrollBars . horizontal , scrollBars . vertical ] . forEach ( scrollBar = > {
@ -252,7 +224,8 @@ export function renderScene(
) ;
}
} ) ;
context . restore ( ) ;
context . fillStyle = fillStyle ;
context . strokeStyle = strokeStyle ;
return { atLeastOneVisibleElement : visibleElements.length > 0 , scrollBars } ;
}
@ -317,16 +290,3 @@ export function renderSceneToSvg(
) ;
} ) ;
}
function getContextTransformScaleX ( transform : DOMMatrix ) : number {
return transform . a ;
}
function getContextTransformScaleY ( transform : DOMMatrix ) : number {
return transform . d ;
}
function getContextTransformTranslateX ( transform : DOMMatrix ) : number {
return transform . e ;
}
function getContextTransformTranslateY ( transform : DOMMatrix ) : number {
return transform . f ;
}