Skip to content

Commit

Permalink
Added keep size of image and svg
Browse files Browse the repository at this point in the history
  • Loading branch information
salgum1114 committed Aug 30, 2024
1 parent 70eaf4a commit e381fc9
Show file tree
Hide file tree
Showing 9 changed files with 599 additions and 68 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-design-editor",
"version": "0.0.56",
"version": "0.0.57",
"description": "Design Editor Tools with React.js + ant.design + fabric.js",
"main": "dist/react-design-editor.min.js",
"typings": "lib/index.d.ts",
Expand Down
430 changes: 430 additions & 0 deletions public/images/sample/chiller.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 65 additions & 10 deletions src/canvas/handlers/Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { defaults } from '../constants';
import { LinkObject } from '../objects/Link';
import { NodeObject } from '../objects/Node';
import { PortObject } from '../objects/Port';
import { SvgObject } from '../objects/Svg';
import {
CanvasOption,
FabricCanvas,
Expand Down Expand Up @@ -616,17 +617,36 @@ class Handler implements HandlerOptions {
* Set the image
* @param {FabricImage} obj
* @param {(File | string)} [source]
* @param {boolean} [keepSize]
* @returns
*/
public setImage = (obj: FabricImage, source?: File | string): Promise<FabricImage> => {
public setImage = (
obj: FabricImage,
source?: File | string,
keepSize?: boolean,
options?: fabric.IImageOptions,
): Promise<FabricImage> => {
const { height, scaleY } = obj;
const renderCallbaack = (imgObj: FabricImage, src: string) => {
if (keepSize) {
const scale = (height * scaleY) / imgObj.height;
imgObj.set({ scaleY: scale, scaleX: scale, src });
}
this.canvas.requestRenderAll();
};
return new Promise(resolve => {
if (!source) {
obj.set('file', null);
obj.set('src', null);
resolve(
obj.setSrc('./images/sample/transparentBg.png', () => this.canvas.renderAll(), {
dirty: true,
}) as FabricImage,
obj.setSrc(
'./images/sample/transparentBg.png',
(imgObj: FabricImage) => renderCallbaack(imgObj, null),
{
dirty: true,
...options,
},
) as FabricImage,
);
}
if (source instanceof File) {
Expand All @@ -635,19 +655,25 @@ class Handler implements HandlerOptions {
obj.set('file', source);
obj.set('src', null);
resolve(
obj.setSrc(reader.result as string, () => this.canvas.renderAll(), {
dirty: true,
}) as FabricImage,
obj.setSrc(
reader.result as string,
(imgObj: FabricImage) => renderCallbaack(imgObj, reader.result as string),
{
dirty: true,
...options,
},
) as FabricImage,
);
};
reader.readAsDataURL(source);
} else {
obj.set('file', null);
obj.set('src', source);
resolve(
obj.setSrc(source, () => this.canvas.renderAll(), {
obj.setSrc(source, (imgObj: FabricImage) => renderCallbaack(imgObj, source), {
dirty: true,
crossOrigin: 'anonymous',
...options,
}) as FabricImage,
);
}
Expand All @@ -660,9 +686,38 @@ class Handler implements HandlerOptions {
* @param {*} source
* @returns
*/
public setImageById = (id: string, source: any) => {
public setImageById = (id: string, source: any, keepSize?: boolean) => {
const findObject = this.findById(id) as FabricImage;
return Promise.resolve(this.setImage(findObject, source));
return Promise.resolve(this.setImage(findObject, source, keepSize));
};

/**
* Set Svg
*
* @param {SvgObject} obj
* @param {(File | string)} [source]
* @param {boolean} [setSvg]
* @param {boolean} [keepSize]
*/
public setSvg = (
obj: SvgObject,
source?: File | string,
isPath?: boolean,
keepSize?: boolean,
): Promise<SvgObject> => {
return new Promise(resolve => {
if (!source) {
resolve(obj.loadSvg({ src: './images/sample/chiller.svg', loadType: 'file', keepSize }));
}
if (source instanceof File) {
const reader = new FileReader();
reader.readAsDataURL(source);
reader.onload = () =>
resolve(obj.loadSvg({ src: reader.result as string, loadType: 'file', keepSize }));
} else {
resolve(obj.loadSvg({ src: source, loadType: isPath ? 'svg' : 'file', keepSize }));
}
});
};

/**
Expand Down
48 changes: 29 additions & 19 deletions src/canvas/objects/Svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { FabricGroup, FabricObject, FabricObjectOption, toObject } from '../util

export type SvgObject = (FabricGroup | FabricObject) & {
loadSvg(option: SvgOption): Promise<SvgObject>;
setFill(value: string): SvgObject;
setStroke(value: string): SvgObject;
setFill(value: string, filter?: (obj: FabricObject) => boolean): SvgObject;
setStroke(value: string, filter?: (obj: FabricObject) => boolean): SvgObject;
};

export interface SvgOption extends FabricObjectOption {
svg?: string;
src?: string;
loadType?: 'file' | 'svg';
keepSize?: boolean;
}

const Svg = fabric.util.createClass(fabric.Group, {
Expand All @@ -18,9 +19,16 @@ const Svg = fabric.util.createClass(fabric.Group, {
this.callSuper('initialize', [], option);
this.loadSvg(option);
},
addSvgElements(objects: FabricObject[], options: any, path: string) {
const createdObj = fabric.util.groupSVGElements(objects, options, path) as SvgObject;
this.set(options);
addSvgElements(objects: FabricObject[], options: SvgOption) {
const createdObj = fabric.util.groupSVGElements(objects, options) as SvgObject;
const { height, scaleY } = this;
const scale = height ? (height * scaleY) / createdObj.height : createdObj.scaleY;
this.set({ ...options, scaleX: scale, scaleY: scale });
if (this._objects?.length) {
(this as FabricGroup).getObjects().forEach(obj => {
this.remove(obj);
});
}
if (createdObj.getObjects) {
(createdObj as FabricGroup).getObjects().forEach(obj => {
this.add(obj);
Expand Down Expand Up @@ -51,36 +59,38 @@ const Svg = fabric.util.createClass(fabric.Group, {
}
this.add(createdObj);
}
this.set({
fill: options.fill,
stroke: options.stroke,
});
this.setCoords();
if (this.canvas) {
this.canvas.requestRenderAll();
}
return this;
},
loadSvg(option: SvgOption) {
const { svg, loadType, fill, stroke } = option;
const { src, loadType, fill, stroke } = option;
return new Promise<SvgObject>(resolve => {
if (loadType === 'svg') {
fabric.loadSVGFromString(svg, (objects, options) => {
resolve(this.addSvgElements(objects, { ...options, fill, stroke }, svg));
fabric.loadSVGFromString(src, (objects, options) => {
resolve(this.addSvgElements(objects, { ...options, fill, stroke }));
});
} else {
fabric.loadSVGFromURL(svg, (objects, options) => {
resolve(this.addSvgElements(objects, { ...options, fill, stroke }, svg));
fabric.loadSVGFromURL(src, (objects, options) => {
resolve(this.addSvgElements(objects, { ...options, fill, stroke }));
});
}
});
},
setFill(value: any) {
this.getObjects().forEach((obj: FabricObject) => obj.set('fill', value));
setFill(value: any, filter: (obj: FabricObject) => boolean = () => true) {
this.getObjects()
.filter(filter)
.forEach((obj: FabricObject) => obj.set('fill', value));
this.canvas.requestRenderAll();
return this;
},
setStroke(value: any) {
this.getObjects().forEach((obj: FabricObject) => obj.set('stroke', value));
setStroke(value: any, filter: (obj: FabricObject) => boolean = () => true) {
this.getObjects()
.filter(filter)
.forEach((obj: FabricObject) => obj.set('stroke', value));
this.canvas.requestRenderAll();
return this;
},
toObject(propertiesToInclude: string[]) {
Expand Down
3 changes: 2 additions & 1 deletion src/editors/imagemap/Descriptors.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@
"type": "svg",
"superType": "svg",
"name": "New SVG",
"loadType": "svg"
"loadType": "file",
"src": "./images/sample/chiller.svg"
}
}
]
Expand Down
12 changes: 9 additions & 3 deletions src/editors/imagemap/ImageMapEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,13 @@ class ImageMapEditor extends Component {
});
return;
}
if (changedKey === 'file' || changedKey === 'src' || changedKey === 'code') {
if (changedKey === 'file' || changedKey === 'src' || changedKey === 'code' || changedKey === 'svg') {
if (selectedItem.type === 'image') {
this.canvasRef.handler.setImageById(selectedItem.id, changedValue);
this.canvasRef.handler.setImageById(selectedItem.id, changedValue, true);
} else if (selectedItem.superType === 'element') {
this.canvasRef.handler.elementHandler.setById(selectedItem.id, changedValue);
} else if (selectedItem.superType === 'svg') {
this.canvasRef.handler.setSvg(selectedItem, changedValue);
}
return;
}
Expand Down Expand Up @@ -331,7 +333,11 @@ class ImageMapEditor extends Component {
}
return;
}
this.canvasRef.handler.set(changedKey, changedValue);
if (selectedItem.type === 'svg' && changedKey === 'fill') {
selectedItem.setFill(changedValue);
} else {
this.canvasRef.handler.set(changedKey, changedValue);
}
},
onChangeWokarea: (changedKey, changedValue, allValues) => {
if (changedKey === 'layout') {
Expand Down
28 changes: 6 additions & 22 deletions src/editors/imagemap/ImageMapItems.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Collapse, notification, Input, message } from 'antd';
import { Collapse, Input, message, notification } from 'antd';
import classnames from 'classnames';
import i18n from 'i18next';
import PropTypes from 'prop-types';
import React, { Component } from 'react';

import { uuid } from 'uuidv4';
import CommonButton from '../../components/common/CommonButton';
import Scrollbar from '../../components/common/Scrollbar';
import { Flex } from '../../components/flex';
import Icon from '../../components/icon/Icon';
import Scrollbar from '../../components/common/Scrollbar';
import CommonButton from '../../components/common/CommonButton';
import { SVGModal } from '../../components/common';
import { uuid } from 'uuidv4';

notification.config({
top: 80,
Expand Down Expand Up @@ -104,17 +103,8 @@ class ImageMapItems extends Component {
}
const id = uuid();
const option = Object.assign({}, item.option, { id });
if (item.option.superType === 'svg' && item.type === 'default') {
this.handlers.onSVGModalVisible(item.option);
return;
}
canvasRef.handler.add(option, centered);
},
onAddSVG: (option, centered) => {
const { canvasRef } = this.props;
canvasRef.handler.add({ ...option, type: 'svg', superType: 'svg', id: uuid(), name: 'New SVG' }, centered);
this.handlers.onSVGModalVisible();
},
onDrawingItem: item => {
const { canvasRef } = this.props;
if (canvasRef.handler.interactionMode === 'polygon') {
Expand Down Expand Up @@ -318,12 +308,6 @@ class ImageMapItems extends Component {
</Flex>
</Scrollbar>
</Flex>
<SVGModal
visible={svgModalVisible}
onOk={this.handlers.onAddSVG}
onCancel={this.handlers.onSVGModalVisible}
option={svgOption}
/>
</div>
);
}
Expand Down
29 changes: 17 additions & 12 deletions src/editors/imagemap/properties/PropertyDefinition.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import MarkerProperty from './MarkerProperty';
import AnimationProperty from './AnimationProperty';
import ChartProperty from './ChartProperty';
import ElementProperty from './ElementProperty';
import GeneralProperty from './GeneralProperty';
import StyleProperty from './StyleProperty';
import TooltipProperty from './TooltipProperty';
import IframeProperty from './IframeProperty';
import ImageFilterProperty from './ImageFilterProperty';
import ImageProperty from './ImageProperty';
import TextProperty from './TextProperty';
import MapProperty from './MapProperty';
import LinkProperty from './LinkProperty';
import VideoProperty from './VideoProperty';
import ElementProperty from './ElementProperty';
import IframeProperty from './IframeProperty';
import AnimationProperty from './AnimationProperty';
import MapProperty from './MapProperty';
import MarkerProperty from './MarkerProperty';
import ShadowProperty from './ShadowProperty';
import UserProperty from './UserProperty';
import StyleProperty from './StyleProperty';
import SvgProperty from './SvgProperty';
import TextProperty from './TextProperty';
import TooltipProperty from './TooltipProperty';
import TriggerProperty from './TriggerProperty';
import ImageFilterProperty from './ImageFilterProperty';
import ChartProperty from './ChartProperty';
import UserProperty from './UserProperty';
import VideoProperty from './VideoProperty';

export default {
map: {
Expand Down Expand Up @@ -394,6 +395,10 @@ export default {
title: 'General',
component: GeneralProperty,
},
svg: {
title: 'SVG',
component: SvgProperty,
},
link: {
title: 'Link',
component: LinkProperty,
Expand Down
40 changes: 40 additions & 0 deletions src/editors/imagemap/properties/SvgProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Form, Radio } from 'antd';
import i18n from 'i18next';
import React from 'react';

import FileUpload from '../../../components/common/FileUpload';

export default {
render(canvasRef, form, data) {
if (!data) {
return null;
}
const loadType = data.loadType || 'file';
return (
<React.Fragment>
<Form.Item label={i18n.t('common.type')}>
{form.getFieldDecorator('loadType', {
initialValue: loadType,
})(
<Radio.Group onChange={this.handleChangeSvgType}>
<Radio.Button value="file">{i18n.t('common.file')}</Radio.Button>
<Radio.Button value="svg">{i18n.t('common.svg')}</Radio.Button>
</Radio.Group>,
)}
</Form.Item>
<Form.Item label={loadType === 'svg' ? i18n.t('common.svg') : i18n.t('common.file')}>
{form.getFieldDecorator('src', {
rules: [
{
required: true,
message: i18n.t('validation.enter-property', {
arg: loadType === 'svg' ? i18n.t('common.svg') : i18n.t('common.file'),
}),
},
],
})(loadType === 'svg' ? <InputHtml /> : <FileUpload accept=".svg" />)}
</Form.Item>
</React.Fragment>
);
},
};

0 comments on commit e381fc9

Please sign in to comment.