fix(tldraw): wrong initial/viewer zoom

Fixes a case when the presentation is just uploaded and a wrong initial zoom was set.
Also fix viewer zoom not correclty adjusting to the area size when zoomed out.
This commit is contained in:
germanocaumo 2022-07-08 15:06:00 +00:00
parent 5748a783a6
commit 217fd6c06e
7 changed files with 26 additions and 76 deletions

View File

@ -1,10 +1,8 @@
import { Meteor } from 'meteor/meteor';
import switchSlide from './methods/switchSlide';
import zoomSlide from './methods/zoomSlide';
import persistAsset from './methods/persistAsset';
Meteor.methods({
switchSlide,
zoomSlide,
persistAsset,
});

View File

@ -1,21 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { extractCredentials } from '/imports/api/common/server/helpers';
import Logger from '/imports/startup/server/logger';
import addAsset from '../modifiers/addAsset';
export default function persistAsset(asset) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'test';
try {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
asset.meetingId = meetingId;
addAsset(asset);
// RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, shape);
} catch (err) {
Logger.error(`Exception while invoking method persistAsset ${err.stack}`);
}
}

View File

@ -1,7 +0,0 @@
import Captions from '/imports/api/captions';
export default function addAsset(asset) {
delete asset._id;
Captions.upsert({ meetingId: asset.meetingId, id: asset.id }, { ...asset })
}

View File

@ -892,6 +892,9 @@ class Presentation extends PureComponent {
showSlide,
isFullscreen,
localPosition,
isZoomed,
presentationWidth,
presentationHeight,
} = this.state;
let viewBoxDimensions;
@ -970,8 +973,10 @@ class Presentation extends PureComponent {
curPageId={currentSlide?.num.toString()}
svgUri={currentSlide?.svgUri}
intl={intl}
presentationBounds={presentationBounds}
presentationWidth={presentationWidth}
presentationHeight={presentationHeight}
isViewersCursorLocked={isViewersCursorLocked}
isZoomed={isZoomed}
setIsZoomed={this.setIsZoomed}
zoomChanger={this.zoomChanger}
/>

View File

@ -2,15 +2,13 @@ import * as React from "react";
import _ from "lodash";
import Cursors from "./cursors/container";
import { TldrawApp, Tldraw } from "@tldraw/tldraw";
import logger from '/imports/startup/client/logger';
import {
ColorStyle,
DashStyle,
SizeStyle,
TDDocument,
TDShapeType,
} from "@tldraw/tldraw";
import { Renderer, Utils } from "@tldraw/core";
import { Utils } from "@tldraw/core";
function usePrevious(value) {
const ref = React.useRef();
@ -31,11 +29,8 @@ export default function Whiteboard(props) {
isPresenter,
removeShapes,
initDefaultPages,
meetingId,
persistShape,
persistAsset,
shapes,
assets,
currentUser,
curPres,
whiteboardId,
@ -45,7 +40,8 @@ export default function Whiteboard(props) {
slidePosition,
curPageId,
svgUri,
presentationBounds,
presentationWidth,
presentationHeight,
isViewersCursorLocked,
setIsZoomed,
zoomChanger,
@ -61,35 +57,28 @@ export default function Whiteboard(props) {
pages,
pageStates,
bindings: {},
assets,
assets: {},
});
//const [doc, setDoc] = React.useState(rDocument.current);
const [_assets, setAssets] = React.useState(assets);
const [command, setCommand] = React.useState("");
const [wbAccess, setWBAccess] = React.useState(props?.hasMultiUserAccess(props.whiteboardId, props.currentUser.userId));
const [selectedIds, setSelectedIds] = React.useState([]);
const [tldrawAPI, setTLDrawAPI] = React.useState(null);
const [cameraFitSlide, setCameraFitSlide] = React.useState({point: [0, 0], zoom: 0});
const prevShapes = usePrevious(shapes);
const prevSlidePosition = usePrevious(slidePosition);
const prevPageId = usePrevious(curPageId);
const calculateCameraFitSlide = () => {
let zoom =
Math.min(
(presentationBounds.width) / slidePosition.width,
(presentationBounds.height) / slidePosition.height
(presentationWidth) / slidePosition.width,
(presentationHeight) / slidePosition.height
);
zoom = Utils.clamp(zoom, 0.1, 5);
let point = [0, 0];
if ((presentationBounds.width / presentationBounds.height) >
if ((presentationWidth / presentationHeight) >
(slidePosition.width / slidePosition.height))
{
point[0] = (presentationBounds.width - (slidePosition.width * zoom)) / 2 / zoom
point[0] = (presentationWidth - (slidePosition.width * zoom)) / 2 / zoom
} else {
point[1] = (presentationBounds.height - (slidePosition.height * zoom)) / 2 / zoom
point[1] = (presentationHeight - (slidePosition.height * zoom)) / 2 / zoom
}
isPresenter && zoomChanger(zoom);
@ -172,9 +161,9 @@ export default function Whiteboard(props) {
}
return currentDoc;
}, [assets, shapes, tldrawAPI, curPageId, slidePosition]);
}, [shapes, tldrawAPI, curPageId, slidePosition]);
// when presentationBounds change, update tldraw camera
// when presentationSizes change, update tldraw camera
// to fit slide on center if zoomed out
React.useEffect(() => {
if (curPageId && slidePosition) {
@ -184,7 +173,7 @@ export default function Whiteboard(props) {
tldrawAPI?.setCamera(camera.point, camera.zoom);
}
}
}, [presentationBounds, curPageId, document?.documentElement?.dir]);
}, [presentationWidth, presentationHeight, curPageId, document?.documentElement?.dir]);
// change tldraw page when presentation page changes
React.useEffect(() => {
@ -388,6 +377,14 @@ export default function Whiteboard(props) {
onPatch={(s, reason) => {
if (reason && isPresenter && (reason.includes("zoomed") || reason.includes("panned"))) {
if (cameraFitSlide.zoom === 0) {
//can happen when the slide finish uploading
const cameraFitSlide = calculateCameraFitSlide();
tldrawAPI?.setCamera(cameraFitSlide.point, cameraFitSlide.zoom);
setIsZoomed(false);
setCameraFitSlide(cameraFitSlide);
return;
}
const camera = tldrawAPI.getPageState().camera;
//don't allow zoom out more than fit
if (camera.zoom <= cameraFitSlide.zoom) {

View File

@ -16,18 +16,15 @@ const WhiteboardContainer = (props) => {
export default withTracker(({ whiteboardId, curPageId, intl, zoomChanger }) => {
const shapes = Service.getShapes(whiteboardId, curPageId, intl);
const assets = Service.getAssets();
const curPres = Service.getCurrentPres();
return {
initDefaultPages: Service.initDefaultPages,
persistShape: Service.persistShape,
persistAsset: Service.persistAsset,
isMultiUserActive: Service.isMultiUserActive,
hasMultiUserAccess: Service.hasMultiUserAccess,
changeCurrentSlide: Service.changeCurrentSlide,
shapes: shapes,
assets: assets,
curPres,
removeShapes: Service.removeShapes,
zoomSlide: PresentationToolbarService.zoomSlide,

View File

@ -1,5 +1,4 @@
import Users from '/imports/api/users';
import Captions from "/imports/api/captions";
import Auth from '/imports/ui/services/auth';
import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user';
import addAnnotationQuery from '/imports/api/annotations/addAnnotation';
@ -13,8 +12,6 @@ const Annotations = new Mongo.Collection(null);
const UnsentAnnotations = new Mongo.Collection(null);
const ANNOTATION_CONFIG = Meteor.settings.public.whiteboard.annotations;
const DRAW_START = ANNOTATION_CONFIG.status.start;
const DRAW_UPDATE = ANNOTATION_CONFIG.status.update;
const DRAW_END = ANNOTATION_CONFIG.status.end;
let annotationsStreamListener = null;
@ -299,8 +296,6 @@ const persistShape = (shape, whiteboardId) => {
sendAnnotation(annotation);
};
const persistAsset = (asset) => makeCall("persistAsset", asset);
const removeShapes = (shapes, whiteboardId) => makeCall("deleteAnnotations", shapes, whiteboardId);
const changeCurrentSlide = (s) => {
@ -355,18 +350,6 @@ const getCurrentPres = () => {
return PresentationService.getCurrentPresentation(podId);
}
const getAssets = () => {
// temporary storage for assets
let a = Captions.find().fetch().filter(s => s.src);
let _assets = {}
Object.entries(a).map(([k,v]) => {
_assets[v.id] = v;
return v.src && v;
});
return _assets;
}
const initDefaultPages = (count = 1) => {
const pages = {};
const pageStates = {};
@ -409,9 +392,7 @@ export {
removeGlobalAccess,
removeIndividualAccess,
persistShape,
persistAsset,
getShapes,
getAssets,
getCurrentPres,
removeShapes,
changeCurrentSlide,