Skip to content

Commit

Permalink
Merge pull request #27 from projectstorm/feature_scaling
Browse files Browse the repository at this point in the history
Feature: image scaling and better initial loading
  • Loading branch information
dylanvorster authored Jul 5, 2023
2 parents 15074d7 + 332ab01 commit 6822267
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 43 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ You can simply paste images you have copied in your clipboard, and then arrange
* Crop images + the ability to re-crop the original image at any stage
* Double click to focus images in the center of the screen
* Fullscreen toggle
* Resize images on any corner

![](./images/screenshot1.png)
![](./images/screenshot2.png)
Expand Down
7 changes: 7 additions & 0 deletions tornado-common/src/api/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ export enum MediaSize {
ORIGINAL = 4
}

export const MEDIA_SIZES = {
[MediaSize.SMALL]: 200,
[MediaSize.MEDIUM]: 500,
[MediaSize.LARGE]: 1000,
[MediaSize.X_LARGE]: 2000
};

export interface GetMediaRequest {
image: number;
size: MediaSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ export const ConceptCanvasWidget: React.FC<ConceptCanvasWidgetProps> = observer(
}
}, []);

const zoomToFix = useCallback(() => {
const zoomToFit = useCallback(() => {
engine.zoomToFitElements({
elements: _.values(engine.getModel().getLayers()[0].getModels()) as unknown as ImageElement[],
margin: 10
margin: 0
});
}, []);

Expand Down Expand Up @@ -74,7 +74,7 @@ export const ConceptCanvasWidget: React.FC<ConceptCanvasWidgetProps> = observer(
return;
}
if (!props.board.canvasTranslateCache) {
zoomToFix();
zoomToFit();
} else {
engine.getModel().setOffsetX(props.board.canvasTranslateCache.offsetX);
engine.getModel().setOffsetY(props.board.canvasTranslateCache.offsetY);
Expand All @@ -97,6 +97,7 @@ export const ConceptCanvasWidget: React.FC<ConceptCanvasWidgetProps> = observer(
finished: (data) => {
element.update(data);
engine.repaintCanvas();
engine.getModel().save();
}
});
});
Expand Down Expand Up @@ -131,7 +132,7 @@ export const ConceptCanvasWidget: React.FC<ConceptCanvasWidgetProps> = observer(
type={ButtonType.DISCRETE}
icon="expand"
action={async () => {
zoomToFix();
zoomToFit();
}}
/>
</S.Controls>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ import {
SelectingState,
State
} from '@projectstorm/react-canvas-core';
import { ResizeElementState } from './ResizeElementState';
import { CornerPosition } from './controls-layer/ControlsElementWidget';
import { ImageElement } from './image-element/ImageElementFactory';

export class DefaultCanvasState extends State<CanvasEngine> {
dragCanvas: DragCanvasState;
resizeElement: ResizeElementState;
dragItems: MoveItemsState;

constructor() {
Expand All @@ -20,6 +24,7 @@ export class DefaultCanvasState extends State<CanvasEngine> {
});
this.childStates = [new SelectingState()];
this.dragCanvas = new DragCanvasState({});
this.resizeElement = new ResizeElementState();
this.dragItems = new MoveItemsState();

// determine what was clicked on
Expand All @@ -29,6 +34,15 @@ export class DefaultCanvasState extends State<CanvasEngine> {
fire: (event: ActionEvent<MouseEvent>) => {
const element = this.engine.getActionEventBus().getModelForEvent(event);

if ((event.event.target as HTMLDivElement).dataset.anchorposition) {
this.resizeElement.setup(
(event.event.target as HTMLDivElement).dataset.anchorposition as CornerPosition,
element as ImageElement
);
this.transitionWithEvent(this.resizeElement, event);
return true;
}

// the canvas was clicked on, transition to the dragging canvas state
if (!element) {
this.transitionWithEvent(this.dragCanvas, event);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { AbstractDisplacementState, AbstractDisplacementStateEvent } from '@projectstorm/react-canvas-core';
import { CornerPosition } from './controls-layer/ControlsElementWidget';
import { ImageElement } from './image-element/ImageElementFactory';

export class ResizeElementState extends AbstractDisplacementState {
position: CornerPosition;
element: ImageElement;
width: number;
height: number;
x: number;
y: number;

constructor() {
super({
name: 'resize-elements'
});
}

setup(position: CornerPosition, element: ImageElement) {
this.position = position;
this.element = element;
this.width = element.width;
this.height = element.height;
this.x = element.getX();
this.y = element.getY();
}

fireMouseMoved(event: AbstractDisplacementStateEvent): any {
if (this.position === CornerPosition.SE) {
const newWidth = this.width + event.virtualDisplacementX;
const newHeight = (newWidth / this.width) * this.height;
this.element.setSize(newWidth, newHeight);
} else if (this.position === CornerPosition.NW) {
const newWidth = this.width - event.virtualDisplacementX;
const newHeight = (newWidth / this.width) * this.height;
this.element.setSize(newWidth, newHeight);
this.element.setPosition(this.x + event.virtualDisplacementX, this.height - newHeight + this.y);
} else if (this.position === CornerPosition.SW) {
const newWidth = this.width - event.virtualDisplacementX;
const newHeight = (newWidth / this.width) * this.height;
this.element.setSize(newWidth, newHeight);
this.element.setPosition(this.x + event.virtualDisplacementX, this.y);
} else if (this.position === CornerPosition.NE) {
const newWidth = this.width + event.virtualDisplacementX;
const newHeight = (newWidth / this.width) * this.height;
this.element.setSize(newWidth, newHeight);
this.element.setPosition(this.x, this.height - newHeight + this.y);
}

this.engine.repaintCanvas();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ export interface ControlsElementWidgetProps {
engine: CanvasEngine;
}

export enum CornerPosition {
NW = 'nw',
NE = 'ne',
SW = 'sw',
SE = 'se'
}

const ANCHOR_SIZE = 14;

export const ControlsElementWidget: React.FC<ControlsElementWidgetProps> = (props) => {
const canvas = (props.model.getParent() as ImageLayerModel).getParent();
const zoom = canvas.getZoomLevel() / 100;
Expand All @@ -21,6 +30,8 @@ export const ControlsElementWidget: React.FC<ControlsElementWidgetProps> = (prop
return null;
}

const offset = (-1 * ANCHOR_SIZE) / 2;

return (
<S.Container
style={{
Expand All @@ -32,7 +43,7 @@ export const ControlsElementWidget: React.FC<ControlsElementWidgetProps> = (prop
>
<S.Controls>
<S.Button
type={ButtonType.NORMAL}
type={ButtonType.DISCRETE}
icon="trash"
instant={true}
action={async () => {
Expand All @@ -41,7 +52,7 @@ export const ControlsElementWidget: React.FC<ControlsElementWidgetProps> = (prop
}}
/>
<S.Button
type={ButtonType.NORMAL}
type={ButtonType.DISCRETE}
icon="crop"
instant={true}
action={async () => {
Expand All @@ -60,6 +71,42 @@ export const ControlsElementWidget: React.FC<ControlsElementWidgetProps> = (prop
}}
/>
</S.Controls>
<S.Anchor
data-anchorposition={CornerPosition.NW}
data-imageid={props.model.getID()}
style={{
top: offset,
left: offset,
cursor: 'nw-resize'
}}
/>
<S.Anchor
data-anchorposition={CornerPosition.NE}
data-imageid={props.model.getID()}
style={{
top: offset,
right: offset,
cursor: 'ne-resize'
}}
/>
<S.Anchor
data-anchorposition={CornerPosition.SE}
data-imageid={props.model.getID()}
style={{
bottom: offset,
right: offset,
cursor: 'se-resize'
}}
/>
<S.Anchor
data-anchorposition={CornerPosition.SW}
data-imageid={props.model.getID()}
style={{
bottom: offset,
left: offset,
cursor: 'sw-resize'
}}
/>
</S.Container>
);
};
Expand All @@ -72,6 +119,16 @@ namespace S {
pointer-events: none;
`;

export const Anchor = styled.div`
box-sizing: border-box;
position: absolute;
border: solid 2px ${(p) => p.theme.editor.selected};
width: ${ANCHOR_SIZE}px;
height: ${ANCHOR_SIZE}px;
background: ${(p) => p.theme.editor.selectedShadow};
pointer-events: all;
`;

export const Controls = styled.div`
position: absolute;
top: -40px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from 'react';
import {
AbstractReactFactory,
BasePositionModel,
BasePositionModelGenerics,
BasePositionModelListener,
DeserializeEvent,
GenerateModelEvent,
GenerateWidgetEvent
Expand All @@ -12,7 +14,11 @@ import { FileData } from '@projectstorm/tornado-common';
import { ConceptCanvasEngine } from '../ConceptCanvasEngine';
import { Rectangle } from '@projectstorm/geometry';

export class ImageElement extends BasePositionModel {
export interface ImageElementListener extends BasePositionModelListener {
sizeUpdated: () => any;
}

export class ImageElement extends BasePositionModel<BasePositionModelGenerics & { LISTENER: ImageElementListener }> {
public width: number;
public height: number;

Expand All @@ -30,11 +36,16 @@ export class ImageElement extends BasePositionModel {
return (this.getParent() as ImageLayerModel).getParent();
}

setSize(width: number, height: number) {
this.width = width;
this.height = height;
this.fireEvent({}, 'sizeUpdated');
}

update(data: FileData) {
const size = 500;
this.imageID = data.id;
this.width = size;
this.height = (size / data.width) * data.height;
this.setSize(size, (size / data.width) * data.height);
}

getBoundingBox(): Rectangle {
Expand Down
Loading

0 comments on commit 6822267

Please sign in to comment.