kopia lustrzana https://github.com/Tldraw/Tldraw
fix up
rodzic
caf9ec4ee1
commit
e929bdd133
|
@ -18,12 +18,12 @@ const CAMERA_OPTIONS: TLCameraOptions = {
|
|||
zoomSpeed: 1,
|
||||
zoomSteps: [0.1, 0.25, 0.5, 1, 2, 4, 8],
|
||||
constraints: {
|
||||
fit: 'max',
|
||||
resetDimension: 'max',
|
||||
bounds: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1200,
|
||||
h: 800,
|
||||
w: 1600,
|
||||
h: 900,
|
||||
},
|
||||
fitX: 'contain',
|
||||
fitY: 'contain',
|
||||
|
@ -281,12 +281,12 @@ const CameraOptionsControlPanel = track(() => {
|
|||
<label htmlFor="fit">Fit</label>
|
||||
<select
|
||||
name="fit"
|
||||
value={constraints.fit}
|
||||
value={constraints.resetDimension}
|
||||
onChange={(e) => {
|
||||
updateOptions({
|
||||
constraints: {
|
||||
...constraints,
|
||||
fit: e.target.value as any,
|
||||
resetDimension: e.target.value as any,
|
||||
},
|
||||
})
|
||||
}}
|
||||
|
|
|
@ -133,7 +133,7 @@ export function ImageAnnotationEditor({
|
|||
editor.setCameraOptions(
|
||||
{
|
||||
constraints: {
|
||||
fit: 'max',
|
||||
resetDimension: 'max',
|
||||
bounds: { w: image.width, h: image.height, x: 0, y: 0 },
|
||||
padding: { x: 32, y: 64 },
|
||||
origin: { x: 0.5, y: 0.5 },
|
||||
|
@ -145,7 +145,7 @@ export function ImageAnnotationEditor({
|
|||
panSpeed: 1,
|
||||
isLocked: false,
|
||||
},
|
||||
{ initial: isInitial }
|
||||
{ reset: isInitial }
|
||||
)
|
||||
|
||||
isInitial = false
|
||||
|
|
|
@ -895,11 +895,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
sendBackward(shapes: TLShape[] | TLShapeId[]): this;
|
||||
sendToBack(shapes: TLShape[] | TLShapeId[]): this;
|
||||
setCamera(point: VecLike, opts?: TLCameraMoveOptions): this;
|
||||
setCameraOptions(options: Partial<TLCameraOptions>, opts?: {
|
||||
force?: boolean;
|
||||
immediate?: boolean;
|
||||
initial?: boolean;
|
||||
}): this;
|
||||
setCameraOptions(options: Partial<TLCameraOptions>, opts?: TLCameraMoveOptions): this;
|
||||
setCroppingShape(shape: null | TLShape | TLShapeId): this;
|
||||
setCurrentPage(page: TLPage | TLPageId, historyOptions?: TLCommandHistoryOptions): this;
|
||||
setCurrentTool(id: string, info?: {}): this;
|
||||
|
@ -1997,8 +1993,8 @@ export type TLBrushProps = {
|
|||
// @public (undocumented)
|
||||
export type TLCameraMoveOptions = Partial<{
|
||||
animation: Partial<{
|
||||
duration: number;
|
||||
easing: (t: number) => number;
|
||||
duration: number;
|
||||
}>;
|
||||
force: boolean;
|
||||
immediate: boolean;
|
||||
|
@ -2013,7 +2009,7 @@ export type TLCameraOptions = {
|
|||
bounds: BoxModel;
|
||||
origin: VecLike;
|
||||
padding: VecLike;
|
||||
fit: 'max' | 'min' | 'none' | 'x' | 'y';
|
||||
resetDimension: 'max' | 'min' | 'none' | 'x' | 'y';
|
||||
};
|
||||
panSpeed: number;
|
||||
zoomSpeed: number;
|
||||
|
|
|
@ -17060,8 +17060,9 @@
|
|||
"text": ", opts?: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n force?: boolean;\n immediate?: boolean;\n initial?: boolean;\n }"
|
||||
"kind": "Reference",
|
||||
"text": "TLCameraMoveOptions",
|
||||
"canonicalReference": "@tldraw/editor!TLCameraMoveOptions:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -36940,14 +36941,14 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<{\n duration: number;\n easing: (t: number) => number;\n }>;\n force: boolean;\n immediate: boolean;\n reset: boolean;\n}>"
|
||||
"text": "<{\n easing: (t: number) => number;\n duration: number;\n }>;\n force: boolean;\n immediate: boolean;\n reset: boolean;\n}>"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/editor/Editor.ts",
|
||||
"fileUrlPath": "packages/editor/src/lib/editor/types/misc-types.ts",
|
||||
"releaseTag": "Public",
|
||||
"name": "TLCameraMoveOptions",
|
||||
"typeTokenRange": {
|
||||
|
@ -36993,7 +36994,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n fit: 'max' | 'min' | 'none' | 'x' | 'y';\n };\n panSpeed: number;\n zoomSpeed: number;\n zoomSteps: number[];\n isLocked: boolean;\n wheelBehavior: 'none' | 'pan' | 'zoom';\n}"
|
||||
"text": ";\n resetDimension: 'max' | 'min' | 'none' | 'x' | 'y';\n };\n panSpeed: number;\n zoomSpeed: number;\n zoomSteps: number[];\n isLocked: boolean;\n wheelBehavior: 'none' | 'pan' | 'zoom';\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
|
|
@ -122,12 +122,7 @@ export {
|
|||
SIDES,
|
||||
SVG_PADDING,
|
||||
} from './lib/constants'
|
||||
export {
|
||||
Editor,
|
||||
type TLCameraMoveOptions,
|
||||
type TLEditorOptions,
|
||||
type TLResizeShapeOptions,
|
||||
} from './lib/editor/Editor'
|
||||
export { Editor, type TLEditorOptions, type TLResizeShapeOptions } from './lib/editor/Editor'
|
||||
export type {
|
||||
SideEffectManager,
|
||||
TLAfterChangeHandler,
|
||||
|
@ -240,6 +235,7 @@ export {
|
|||
} from './lib/editor/types/history-types'
|
||||
export {
|
||||
type RequiredKeys,
|
||||
type TLCameraMoveOptions,
|
||||
type TLCameraOptions,
|
||||
type TLSvgOptions,
|
||||
} from './lib/editor/types/misc-types'
|
||||
|
|
|
@ -134,20 +134,15 @@ import {
|
|||
} from './types/event-types'
|
||||
import { TLExternalAssetContent, TLExternalContent } from './types/external-content'
|
||||
import { TLCommandHistoryOptions } from './types/history-types'
|
||||
import { OptionalKeys, RequiredKeys, TLCameraOptions, TLSvgOptions } from './types/misc-types'
|
||||
import {
|
||||
OptionalKeys,
|
||||
RequiredKeys,
|
||||
TLCameraMoveOptions,
|
||||
TLCameraOptions,
|
||||
TLSvgOptions,
|
||||
} from './types/misc-types'
|
||||
import { TLResizeHandle } from './types/selection-types'
|
||||
|
||||
/** @public */
|
||||
export type TLCameraMoveOptions = Partial<{
|
||||
animation: Partial<{
|
||||
duration: number
|
||||
easing: (t: number) => number
|
||||
}>
|
||||
immediate: boolean
|
||||
force: boolean
|
||||
reset: boolean
|
||||
}>
|
||||
|
||||
/** @public */
|
||||
export type TLResizeShapeOptions = Partial<{
|
||||
initialBounds: Box
|
||||
|
@ -2079,7 +2074,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
* @public */
|
||||
getCameraFitZoom() {
|
||||
const cameraOptions = this.getCameraOptions()
|
||||
if (!cameraOptions.constraints || cameraOptions.constraints.fit === 'none') {
|
||||
if (!cameraOptions.constraints || cameraOptions.constraints.resetDimension === 'none') {
|
||||
return 1
|
||||
}
|
||||
const { padding } = cameraOptions.constraints
|
||||
|
@ -2090,7 +2085,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const zx = (vsb.w - px * 2) / bounds.w
|
||||
const zy = (vsb.h - py * 2) / bounds.h
|
||||
|
||||
switch (cameraOptions.constraints.fit) {
|
||||
switch (cameraOptions.constraints.resetDimension) {
|
||||
case 'min': {
|
||||
return Math.max(zx, zy)
|
||||
}
|
||||
|
@ -2104,7 +2099,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
return zy
|
||||
}
|
||||
default: {
|
||||
throw exhaustiveSwitchError(cameraOptions.constraints.fit)
|
||||
throw exhaustiveSwitchError(cameraOptions.constraints.resetDimension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2137,10 +2132,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
* @param opts - The options for the change.
|
||||
*
|
||||
* @public */
|
||||
setCameraOptions(
|
||||
options: Partial<TLCameraOptions>,
|
||||
opts?: { immediate?: boolean; force?: boolean; initial?: boolean }
|
||||
) {
|
||||
setCameraOptions(options: Partial<TLCameraOptions>, opts?: TLCameraMoveOptions) {
|
||||
const next = { ...this._cameraOptions.__unsafe__getWithoutCapture(), ...options }
|
||||
if (next.zoomSteps?.length < 1) next.zoomSteps = [1]
|
||||
this._cameraOptions.set(next)
|
||||
|
@ -2149,10 +2141,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
|
||||
/** @internal */
|
||||
private _setCamera(
|
||||
point: VecLike,
|
||||
opts?: { immediate?: boolean; force?: boolean; initial?: boolean }
|
||||
): this {
|
||||
private _setCamera(point: VecLike, opts?: TLCameraMoveOptions): this {
|
||||
const currentCamera = this.getCamera()
|
||||
|
||||
let { x, y, z = currentCamera.z } = point
|
||||
|
@ -2168,12 +2157,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const zoomMin = cameraOptions.zoomSteps[0]
|
||||
const zoomMax = last(cameraOptions.zoomSteps)!
|
||||
|
||||
const vsb = this.getViewportScreenBounds()
|
||||
|
||||
// If bounds are provided, then we'll keep those bounds on screen
|
||||
if (cameraOptions.constraints) {
|
||||
const { constraints } = cameraOptions
|
||||
|
||||
const vsb = this.getViewportScreenBounds()
|
||||
|
||||
// Get padding (it's either a number or an array of 2 numbers for t/b, l/r)
|
||||
// Clamp padding to half the viewport size on either dimension
|
||||
const py = Math.min(constraints.padding.y, vsb.w / 2)
|
||||
|
@ -2193,7 +2182,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
let fitZoom = 1
|
||||
|
||||
switch (cameraOptions.constraints.fit) {
|
||||
switch (cameraOptions.constraints.resetDimension) {
|
||||
case 'min': {
|
||||
fitZoom = Math.max(zx, zy)
|
||||
break
|
||||
|
@ -2215,7 +2204,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const maxZ = zoomMax * fitZoom
|
||||
const minZ = zoomMin * fitZoom
|
||||
|
||||
if (opts?.initial) {
|
||||
if (opts?.reset) {
|
||||
z = fitZoom
|
||||
}
|
||||
|
||||
|
@ -2233,16 +2222,22 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
y = cy + cyB - cyA
|
||||
}
|
||||
|
||||
// Calculate available space
|
||||
const minX = px / z - bounds.x
|
||||
const minY = py / z - bounds.y
|
||||
const freeW = (vsb.w - px * 2) / z - bounds.w
|
||||
const freeH = (vsb.h - py * 2) / z - bounds.h
|
||||
const originX = minX + freeW * constraints.origin.x
|
||||
const originY = minY + freeH * constraints.origin.y
|
||||
|
||||
// x axis
|
||||
|
||||
const minX = px / z - bounds.x
|
||||
const freeW = (vsb.w - px * 2) / z - bounds.w
|
||||
const originX = minX + freeW * constraints.origin.x
|
||||
|
||||
if (opts?.initial) {
|
||||
// Center according to the origin
|
||||
if (opts?.reset) {
|
||||
// Reset the camera according to the origin
|
||||
x = originX
|
||||
y = originY
|
||||
} else {
|
||||
// Apply constraints to the camera
|
||||
switch (constraints.fitX) {
|
||||
case 'lock': {
|
||||
// Center according to the origin
|
||||
|
@ -2269,17 +2264,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// y axis
|
||||
// y axis
|
||||
|
||||
const minY = py / z - bounds.y
|
||||
const freeH = (vsb.h - py * 2) / z - bounds.h
|
||||
const originY = minY + freeH * constraints.origin.y
|
||||
|
||||
if (opts?.initial) {
|
||||
y = originY
|
||||
} else {
|
||||
switch (constraints.fitY) {
|
||||
case 'lock': {
|
||||
y = originY
|
||||
|
@ -2303,9 +2290,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
} else {
|
||||
// constrain the zoom, preserving the center
|
||||
|
||||
if (z > zoomMax || z < zoomMin) {
|
||||
const vsb = this.getViewportScreenBounds()
|
||||
const { x: cx, y: cy, z: cz } = currentCamera
|
||||
const cxA = -cx + vsb.w / cz / 2
|
||||
const cyA = -cy + vsb.h / cz / 2
|
||||
|
@ -2756,11 +2741,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const { isLocked, panSpeed } = this.getCameraOptions()
|
||||
if (isLocked) return this
|
||||
const { x: cx, y: cy, z: cz } = this.getCamera()
|
||||
this.setCamera(
|
||||
new Vec(cx + (offset.x * panSpeed) / cz, cy + (offset.y * panSpeed) / cz, cz),
|
||||
opts
|
||||
)
|
||||
this._flushEventsForTick(0)
|
||||
this.setCamera(new Vec(cx + (offset.x * panSpeed) / cz, cy + (offset.y * panSpeed) / cz, cz), {
|
||||
...opts,
|
||||
immediate: true,
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,23 @@ export type TLSvgOptions = {
|
|||
preserveAspectRatio: React.SVGAttributes<SVGSVGElement>['preserveAspectRatio']
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type TLCameraMoveOptions = Partial<{
|
||||
/** Whether to move the camera immediately, rather than on the next tick. */
|
||||
immediate: boolean
|
||||
/** Whether to force the camera to move, even if the user's camera options have locked the camera. */
|
||||
force: boolean
|
||||
/** Whether to reset the camera to its default position and zoom. */
|
||||
reset: boolean
|
||||
/** An (optional) animation to use. */
|
||||
animation: Partial<{
|
||||
/** The time the animation should take to arrive at the specified camera coordinates. */
|
||||
duration: number
|
||||
/** An easing function to apply to the animation's progress from start to end. */
|
||||
easing: (t: number) => number
|
||||
}>
|
||||
}>
|
||||
|
||||
/** @public */
|
||||
export type TLCameraOptions = {
|
||||
wheelBehavior: 'zoom' | 'pan' | 'none'
|
||||
|
@ -30,8 +47,8 @@ export type TLCameraOptions = {
|
|||
isLocked: boolean
|
||||
/** The camera constraints */
|
||||
constraints?: {
|
||||
/** The type of constraint behavior. */
|
||||
fit: 'min' | 'max' | 'x' | 'y' | 'none'
|
||||
/** Which dimension to fit when the camera is reset. */
|
||||
resetDimension: 'min' | 'max' | 'x' | 'y' | 'none'
|
||||
/** The behavior for the constraints on the x axis. */
|
||||
fitX: 'contain' | 'inside' | 'outside' | 'lock'
|
||||
/** The behavior for the constraints on the y axis. */
|
||||
|
|
Ładowanie…
Reference in New Issue