Use smarter rounding for shape container div width/height (#1930)
This is a follow up to #1915 which caused the shape container div dimensions to be wildly inaccurate. We thought it wouldn't matter but we had a note on discord from someone who was relying on the div container being accurate. This rounds the shape dimensions to the nearest integer that is compatible with the user's device pixel ratio, using the method pioneered in #1858 (thanks @BrianHung) ### Change Type - [x] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Release Notes - Improves the precision of the shape dimensions rounding logic
This commit is contained in:
parent
5dc1436d80
commit
0e621e3de1
2 changed files with 15 additions and 9 deletions
|
@ -2,6 +2,7 @@ import { track, useQuickReactor, useStateTracking } from '@tldraw/state'
|
|||
import { TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import * as React from 'react'
|
||||
import { ShapeUtil } from '../editor/shapes/ShapeUtil'
|
||||
import { nearestMultiple } from '../hooks/useDPRMultiple'
|
||||
import { useEditor } from '../hooks/useEditor'
|
||||
import { useEditorComponents } from '../hooks/useEditorComponents'
|
||||
import { Matrix2d } from '../primitives/Matrix2d'
|
||||
|
@ -79,14 +80,18 @@ export const Shape = track(function Shape({
|
|||
if (!shape) return null
|
||||
|
||||
const bounds = editor.getShapeGeometry(shape).bounds
|
||||
setProperty(
|
||||
'width',
|
||||
`calc(${Math.max(1, Math.ceil(bounds.width)) + 'px'} * var(--tl-dpr-multiple))`
|
||||
)
|
||||
setProperty(
|
||||
'height',
|
||||
`calc(${Math.max(1, Math.ceil(bounds.height)) + 'px'} * var(--tl-dpr-multiple))`
|
||||
)
|
||||
const dpr = editor.instanceState.devicePixelRatio
|
||||
// dprMultiple is the smallest number we can multiply dpr by to get an integer
|
||||
// it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
|
||||
const dprMultiple = nearestMultiple(dpr)
|
||||
// We round the shape width and height up to the nearest multiple of dprMultiple to avoid the browser
|
||||
// making miscalculations when applying the transform.
|
||||
const widthRemainder = bounds.w % dprMultiple
|
||||
const width = widthRemainder === 0 ? bounds.w : bounds.w + (dprMultiple - widthRemainder)
|
||||
const heightRemainder = bounds.h % dprMultiple
|
||||
const height = heightRemainder === 0 ? bounds.h : bounds.h + (dprMultiple - heightRemainder)
|
||||
setProperty('width', Math.max(width, dprMultiple) + 'px')
|
||||
setProperty('height', Math.max(height, dprMultiple) + 'px')
|
||||
},
|
||||
[editor]
|
||||
)
|
||||
|
|
|
@ -8,7 +8,8 @@ function gcd(a: number, b: number): number {
|
|||
return b === 0 ? a : gcd(b, a % b)
|
||||
}
|
||||
|
||||
function nearestMultiple(float: number) {
|
||||
// Returns the lowest value that the given number can be multiplied by to reach an integer
|
||||
export function nearestMultiple(float: number) {
|
||||
const decimal = float.toString().split('.')[1]
|
||||
if (!decimal) return 1
|
||||
const denominator = Math.pow(10, decimal.length)
|
||||
|
|
Loading…
Reference in a new issue