Skip to content

Commit

Permalink
Add scale factor buttons to the editor component
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiaslehnertum committed Oct 2, 2023
1 parent 0279eb9 commit 807c322
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 46 deletions.
31 changes: 19 additions & 12 deletions public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ aside.sidebar section>div {
justify-content: space-between;
}

.switch button {
width: 100%;
button {
background: var(--apollon-background);
color: var(--apollon-primary-contrast);
border: 1px solid var(--apollon-gray);
Expand All @@ -156,6 +155,24 @@ aside.sidebar section>div {
outline: none;
}

button:hover {
background-color: var(--apollon-gray);
border-color: var(--apollon-gray-variant);
}

button:active {
background-color: var(--apollon-gray);
border-color: var(--apollon-gray-variant);
}

.button-rounded {
border-radius: 0.25rem;
}

.switch button {
width: 100%;
}

.switch button:first-child {
border-top-left-radius: 0.25rem;
border-bottom-left-radius: 0.25rem;
Expand All @@ -167,16 +184,6 @@ aside.sidebar section>div {
border-bottom-right-radius: 0.25rem;
}

.switch button:hover {
background-color: var(--apollon-gray);
border-color: var(--apollon-gray-variant);
}

.switch button:active {
background-color: var(--apollon-gray);
border-color: var(--apollon-gray-variant);
}

.switch button.selected {
background-color: var(--apollon-gray);
border-color: var(--apollon-gray-variant);
Expand Down
4 changes: 1 addition & 3 deletions src/main/components/canvas/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { UMLElementState } from '../../services/uml-element/uml-element-types';
import { UMLRelationship } from '../../services/uml-relationship/uml-relationship';
import { EditorRepository } from '../../services/editor/editor-repository';


type OwnProps = {};

type StateProps = {
Expand All @@ -29,15 +28,14 @@ const enhance = connect<StateProps, DispatchProps, OwnProps, ModelState>(
(state) => ({
diagram: state.diagram,
isStatic: state.editor.readonly,
elements: state.elements
elements: state.elements,
}),
null,
null,
{ forwardRef: true },
);

export class CanvasComponent extends Component<Props> implements Omit<ILayer, 'layer'> {

layer: RefObject<SVGSVGElement> = createRef();

origin = (): Point => {
Expand Down
93 changes: 63 additions & 30 deletions src/main/components/canvas/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { ModelState } from '../store/model-state';
import isMobile from 'is-mobile';
import { UMLElementRepository } from '../../services/uml-element/uml-element-repository';
import { AsyncDispatch } from '../../utils/actions/actions';
import {EditorRepository} from '../../services/editor/editor-repository';
import { EditorRepository } from '../../services/editor/editor-repository';
import { clamp } from '../../utils/clamp';
import { ZoomPane } from './zoom-pane';

const minScale: number = 0.5;
const maxScale: number = 5.0;
Expand All @@ -16,8 +18,8 @@ const borderWidth: number = 1;

const StyledEditor = styled.div<{ zoomFactor?: number }>`
display: block;
width: ${100 / minScale}%;
height: ${100 / minScale}%;
width: 100%;
height: 100%;
position: relative;
min-height: inherit;
max-height: inherit;
Expand All @@ -40,6 +42,7 @@ const StyledEditor = styled.div<{ zoomFactor?: number }>`
linear-gradient(to bottom, ${(props) => props.theme.color.gray} 1px, transparent 1px);
background-repeat: repeat;
background-attachment: local;
transition: transform 500ms;
transform-origin: top left;
transform: scale(${(props) => props.zoomFactor ?? 1});
`;
Expand Down Expand Up @@ -84,18 +87,21 @@ const SCROLL_DISTANCE = 5;
class EditorComponent extends Component<Props, State> {
state = getInitialState();
editor = createRef<HTMLDivElement>();
zoomContainer = createRef<HTMLDivElement>();

componentDidMount() {

const {zoomFactor = 1} = this.props;

window.addEventListener('wheel', (event) => {
event.preventDefault();

if (event.ctrlKey) {
this.props.changeZoomFactor(this.clamp(zoomFactor - event.deltaY * 0.01, minScale, maxScale));
}
});
const { zoomFactor = 1 } = this.props;

window.addEventListener(
'wheel',
(event) => {
if (event.ctrlKey) {
event.preventDefault();
this.props.changeZoomFactor(clamp(zoomFactor - event.deltaY * 0.1, minScale, maxScale));
}
},
{ passive: false },
);

window.addEventListener('gesturestart', (event) => {
event.preventDefault();
Expand All @@ -108,9 +114,28 @@ class EditorComponent extends Component<Props, State> {

window.addEventListener('gesturechange', (event) => {
event.preventDefault();
this.props.changeZoomFactor(
this.clamp(this.state.gestureStartZoomFactor * (event as any).scale, minScale, maxScale),
);

const { zoomFactor = 1 } = this.props;

const relativeCursorOffsetX = (event as any).clientX - this.editor.current!.getBoundingClientRect().x;
const relativeCursorOffsetY = (event as any).clientY - this.editor.current!.getBoundingClientRect().y;

const newZoomFactor = clamp(this.state.gestureStartZoomFactor * (event as any).scale, minScale, maxScale);

// console.log(this.state.gestureStartZoomFactor * relativeCursorOffsetX);
// console.log(this.state.gestureStartZoomFactor * relativeCursorOffsetY - newZoomFactor * relativeCursorOffsetY);

// TODO: Evaluate if we need to integrate the zoom Factor here additionally as the relative position changes on zoom

/*
this.zoomContainer.current?.scrollBy({
left: newZoomFactor * relativeCursorOffsetX - zoomFactor * relativeCursorOffsetX ,
top: newZoomFactor * relativeCursorOffsetY - zoomFactor * relativeCursorOffsetY,
behavior: 'smooth'
});
*/

this.props.changeZoomFactor(newZoomFactor);
});

window.addEventListener('gestureend', function (event) {
Expand All @@ -137,26 +162,34 @@ class EditorComponent extends Component<Props, State> {
const { moving, connecting, reconnecting, zoomFactor = 1.0, ...props } = this.props;

if (this.state.isMobile) {

return (
<div ref={this.zoomContainer} style={{ height: '100%', width: '100%', overflow: 'auto' }}>
<StyledEditor ref={this.editor} {...props} onTouchMove={this.customScrolling} zoomFactor={zoomFactor} />
<ZoomPane
value={zoomFactor}
onChange={(zoomFactor) => this.props.changeZoomFactor(zoomFactor)}
min={minScale}
max={maxScale}
/>
</div>
);
} else {
return (
<div style={{height: '100%', width: '100%', overflow: 'auto'}}>
<StyledEditor {...props} zoomFactor={zoomFactor} />
</div>
<div ref={this.zoomContainer} style={{ height: '100%', width: '100%', overflow: 'auto' }}>
<StyledEditor ref={this.editor} {...props} zoomFactor={zoomFactor} />
<ZoomPane
value={zoomFactor}
onChange={(zoomFactor) => this.props.changeZoomFactor(zoomFactor)}
min={minScale}
max={maxScale}
/>
</div>
);
}
}

clamp = (value: number, min: number, max: number): number => {
return Math.max(min, Math.min(value, max));
};

customScrolling = (event: React.TouchEvent) => {

const {zoomFactor = 1} = this.props;
const { zoomFactor = 1 } = this.props;

if (this.editor.current) {
const clientRect = this.editor.current.getBoundingClientRect();
Expand All @@ -165,15 +198,15 @@ class EditorComponent extends Component<Props, State> {

// scroll when on the edge of the element
const scrollHorizontally =
(touch.clientX * zoomFactor) < clientRect.x + SCROLL_BORDER
touch.clientX * zoomFactor < clientRect.x + SCROLL_BORDER
? -SCROLL_DISTANCE
: (touch.clientX * zoomFactor) > clientRect.x + clientRect.width - SCROLL_BORDER
: touch.clientX * zoomFactor > clientRect.x + clientRect.width - SCROLL_BORDER
? SCROLL_DISTANCE
: 0;
const scrollVertically =
(touch.clientY * zoomFactor) < clientRect.y + SCROLL_BORDER
touch.clientY * zoomFactor < clientRect.y + SCROLL_BORDER
? -SCROLL_DISTANCE
: (touch.clientY * zoomFactor)> clientRect.y + clientRect.height - SCROLL_BORDER
: touch.clientY * zoomFactor > clientRect.y + clientRect.height - SCROLL_BORDER
? SCROLL_DISTANCE
: 0;
this.editor.current.scrollBy(scrollHorizontally, scrollVertically);
Expand Down
31 changes: 31 additions & 0 deletions src/main/components/canvas/zoom-pane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { FunctionComponent } from 'react';
import { clamp } from '../../utils/clamp';

type Props = {
min?: number;
max?: number;
step?: number;
value: number;
onChange: (value: number) => void;
};

export const ZoomPaneComponent: FunctionComponent<Props> = (props) => {
const { min = 0.5, max = 5, step = 0.5, value, onChange } = props;

return (
<div style={{ position: 'absolute', left: '0.75em', bottom: '0.75em' }}>
<button
style={{ marginRight: '0.5em' }}
className="button-rounded"
onClick={() => onChange(clamp(value + step, min, max))}
>
+
</button>
<button className="button-rounded" onClick={() => onChange(clamp(value - step, min, max))}>
-
</button>
</div>
);
};

export const ZoomPane = ZoomPaneComponent;
1 change: 0 additions & 1 deletion src/main/components/draggable/draggable-layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ class DraggableLayerComponent extends Component<PropsWithChildren & Props, State
position: this.state.position.subtract(
this.props.canvas
.origin()
.scale(zoomFactor)
.subtract(this.props.root.getBoundingClientRect().x, this.props.root.getBoundingClientRect().y),
),
};
Expand Down
9 changes: 9 additions & 0 deletions src/main/utils/clamp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Clamp a given value to an allowed range
* @param value The value that should be clamped
* @param min The minimum allowed value
* @param max The maximum allowed value
*/
export const clamp = (value: number, min: number, max: number): number => {
return Math.max(min, Math.min(value, max));
};

0 comments on commit 807c322

Please sign in to comment.