|
@@ -1,985 +0,0 @@
|
|
|
-/* eslint-disable camelcase */
|
|
|
-import React from 'react'
|
|
|
-import { Button, Form, Input, Select, Slider, Switch, InputNumber } from 'antd'
|
|
|
-import fabric from 'fabric'
|
|
|
-import cloneDeep from 'lodash.clonedeep'
|
|
|
-import throttle from 'lodash.throttle'
|
|
|
-import RcColorPicker from 'rc-color-picker'
|
|
|
-import 'rc-color-picker/assets/index.css'
|
|
|
-import jrQrcode from 'jr-qrcode'
|
|
|
-
|
|
|
-import { optionArr, newOptionArr } from './util/data.js'
|
|
|
-
|
|
|
-import styles from './index.less'
|
|
|
-
|
|
|
-fabric = fabric.fabric
|
|
|
-const { Option } = Select
|
|
|
-
|
|
|
-const QRErrorCorrectLevel = {
|
|
|
- L: 1,
|
|
|
- M: 0,
|
|
|
- Q: 3,
|
|
|
- H: 2
|
|
|
-}
|
|
|
-
|
|
|
-const _config = {
|
|
|
- canvasState: [],
|
|
|
- currentStateIndex: -1,
|
|
|
- undoStatus: false,
|
|
|
- redoStatus: false,
|
|
|
- undoFinishedStatus: 1,
|
|
|
- redoFinishedStatus: 1
|
|
|
-}
|
|
|
-
|
|
|
-export default class Edit extends React.PureComponent {
|
|
|
- state={
|
|
|
- name: '模版一',
|
|
|
- description: '描述',
|
|
|
- canvas: {},
|
|
|
-
|
|
|
- currentOptionArr: cloneDeep(newOptionArr), // 当前可设置的数组的值
|
|
|
- optionArr: cloneDeep(newOptionArr) // 当前可设置的数组的值
|
|
|
- // currentObjectType: 'text' // 当前要添加对象的类型
|
|
|
- }
|
|
|
-
|
|
|
- activeObject={}
|
|
|
- currentOptionArr=cloneDeep(newOptionArr)
|
|
|
-
|
|
|
- componentDidMount () {
|
|
|
- this.canvas_sprite = new fabric.Canvas('canvas', {
|
|
|
- ...this.state.currentOptionArr[0].css,
|
|
|
- backgroundColor: this.state.currentOptionArr[0].css.background
|
|
|
- })
|
|
|
- this.addEventListener()
|
|
|
- }
|
|
|
-
|
|
|
- async addShape (index, action) {
|
|
|
- const currentOptionArr = this.state.currentOptionArr
|
|
|
- const { type } = currentOptionArr[index]
|
|
|
-
|
|
|
- let Shape
|
|
|
- switch (type) {
|
|
|
- case 'text':
|
|
|
- Shape = await this.addTextObject(index, action)
|
|
|
- break
|
|
|
- case 'image':
|
|
|
- Shape = await this.addImageObject(index, action)
|
|
|
- break
|
|
|
- case 'qrcode':
|
|
|
- Shape = await this.addQrcodeObject(index, action)
|
|
|
- break
|
|
|
- default:
|
|
|
- break
|
|
|
- }
|
|
|
- this.canvas_sprite.setActiveObject(Shape)
|
|
|
- this.activeObject = Shape
|
|
|
- // this.setState({
|
|
|
- // visible: true
|
|
|
- // })
|
|
|
- this.canvas_sprite.add(Shape)
|
|
|
- if (action !== 'update') {
|
|
|
- this.changeActiveObjectValue()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async addTextObject (index, action) {
|
|
|
- let currentOptionArr
|
|
|
- if (action === 'update') {
|
|
|
- currentOptionArr = this.state.currentOptionArr
|
|
|
- } else {
|
|
|
- currentOptionArr = this.currentOptionArr
|
|
|
- }
|
|
|
- const { css } = currentOptionArr[index]
|
|
|
- let {
|
|
|
- width,
|
|
|
- text,
|
|
|
- color,
|
|
|
- fontSize,
|
|
|
- left,
|
|
|
- top,
|
|
|
- fontWeight,
|
|
|
- fontFamily,
|
|
|
- padding,
|
|
|
- textDecoration,
|
|
|
- borderRadius,
|
|
|
- borderWidth,
|
|
|
- borderColor,
|
|
|
- rotate,
|
|
|
- // align,
|
|
|
- shadow,
|
|
|
- lineHeight,
|
|
|
- textAlign,
|
|
|
- maxLines,
|
|
|
- textStyle,
|
|
|
- background
|
|
|
- } = css
|
|
|
- width = width / 1
|
|
|
- left = left / 1
|
|
|
- top = top / 1
|
|
|
- borderRadius = borderRadius / 1
|
|
|
- borderWidth = borderWidth / 1
|
|
|
- rotate = rotate / 1
|
|
|
- fontSize = fontSize / 1
|
|
|
- maxLines = maxLines / 1
|
|
|
- padding = 0
|
|
|
- lineHeight = lineHeight / 1 // 和painter调试得出的值
|
|
|
- shadow = shadow
|
|
|
- .trim()
|
|
|
- .split(/\s+/)
|
|
|
- .join(' ')
|
|
|
- // let Shape
|
|
|
- const config = {
|
|
|
- width, // 文字的高度随行高
|
|
|
- fill: color,
|
|
|
- fontWeight,
|
|
|
- left: 0, // 距离画布左侧的距离,单位是像素
|
|
|
- top: 0,
|
|
|
- fontSize, // 文字大小
|
|
|
- fontFamily,
|
|
|
- padding,
|
|
|
- [textDecoration]: true,
|
|
|
- textAlign,
|
|
|
- textStyle,
|
|
|
- shadow,
|
|
|
- myshadow: shadow,
|
|
|
- splitByGrapheme: true, // 文字换行
|
|
|
- lineHeight,
|
|
|
- editable: true,
|
|
|
- maxLines: maxLines,
|
|
|
- textDecoration: textDecoration,
|
|
|
- lockScalingY: true,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center'
|
|
|
- }
|
|
|
- // 镂空
|
|
|
- // if (textStyle === 'stroke') {
|
|
|
- // config = {
|
|
|
- // ...config,
|
|
|
- // stroke: color,
|
|
|
- // fill: 'rgba(0,0,0)'
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- const textBox = new fabric.Textbox(text, config)
|
|
|
- textBox.toObject = (function (toObject) {
|
|
|
- return function () {
|
|
|
- return fabric.util.object.extend(toObject.call(this), {
|
|
|
- maxLines,
|
|
|
- textDecoration,
|
|
|
- textStyle
|
|
|
- })
|
|
|
- }
|
|
|
- })(textBox.toObject)
|
|
|
- // 通过最大行高计算高度,并删除多余文字,多出文字..表示,三个会换行
|
|
|
- if (textBox.textLines.length > maxLines) {
|
|
|
- let text = ''
|
|
|
- for (let index = 0; index < maxLines; index++) {
|
|
|
- const element = textBox.textLines[index]
|
|
|
- if (index === maxLines - 1) {
|
|
|
- text = text + element + '...'
|
|
|
- } else {
|
|
|
- text += element
|
|
|
- }
|
|
|
- }
|
|
|
- textBox.set({
|
|
|
- text
|
|
|
- })
|
|
|
- if (textBox.textLines.length > maxLines) {
|
|
|
- let text = ''
|
|
|
- for (let index = 0; index < maxLines; index++) {
|
|
|
- const element = textBox.textLines[index]
|
|
|
- if (index === maxLines - 1) {
|
|
|
- text = text + element.substring(0, element.length - 3) + '...'
|
|
|
- } else {
|
|
|
- text += element
|
|
|
- }
|
|
|
- }
|
|
|
- textBox.set({
|
|
|
- text
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- const height = textBox.height / 1 + (textBox.lineHeight / 1 - 1) * textBox.fontSize + padding * 2
|
|
|
- left = css.left - padding + borderWidth
|
|
|
- top = css.top - padding + borderWidth
|
|
|
- textBox.set({
|
|
|
- top: -((textBox.lineHeight / 1 - 1) * textBox.fontSize) / 2
|
|
|
- })
|
|
|
-
|
|
|
- // const Rect = new fabric.Rect({
|
|
|
- // width: width + borderWidth,
|
|
|
- // height: height + borderWidth,
|
|
|
- // left: 0, // 距离画布左侧的距离,单位是像素
|
|
|
- // top: 0,
|
|
|
- // originX: 'center',
|
|
|
- // originY: 'center',
|
|
|
- // // padding,
|
|
|
- // rx: borderRadius,
|
|
|
- // strokeWidth: borderWidth / 1,
|
|
|
- // stroke: borderColor,
|
|
|
- // fill: background,
|
|
|
- // shadow,
|
|
|
- // selectable: false
|
|
|
- // })
|
|
|
- // this.canvas_sprite.add(Rect);
|
|
|
- // let gradientOption = ''
|
|
|
- // if (GD.api.isGradient(background)) {
|
|
|
- // alert(1)
|
|
|
- // gradientOption = GD.api.doGradient(background, width, height)
|
|
|
- // } else {
|
|
|
- // alert(2)
|
|
|
- // }
|
|
|
- // if (gradientOption) Rect.setGradient('fill', gradientOption)
|
|
|
- const Shape = new fabric.Group([], {
|
|
|
- width: width + borderWidth,
|
|
|
- height: height + borderWidth,
|
|
|
- left: left + width / 2, // 距离画布左侧的距离,单位是像素
|
|
|
- top: top + height / 2,
|
|
|
- angle: rotate,
|
|
|
- mytype: 'textGroup',
|
|
|
- oldText: text,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- rx: borderRadius,
|
|
|
- strokeWidth: borderWidth / 1,
|
|
|
- stroke: borderColor,
|
|
|
- fill: background,
|
|
|
- shadow,
|
|
|
- myshadow: shadow,
|
|
|
- lockScalingY: true
|
|
|
- })
|
|
|
- // Shape.add(Rect)
|
|
|
- Shape.add(textBox)
|
|
|
-
|
|
|
- Shape.toObject = (function (toObject) {
|
|
|
- return function () {
|
|
|
- return fabric.util.object.extend(toObject.call(this), {
|
|
|
- mytype: 'textGroup',
|
|
|
- oldText: text,
|
|
|
- rx: borderRadius,
|
|
|
- myshadow: shadow
|
|
|
- })
|
|
|
- }
|
|
|
- })(Shape.toObject)
|
|
|
- return Shape
|
|
|
- }
|
|
|
-
|
|
|
- async addImageObject (index, action) {
|
|
|
- let currentOptionArr
|
|
|
- if (action === 'update') {
|
|
|
- currentOptionArr = this.state.currentOptionArr
|
|
|
- } else {
|
|
|
- currentOptionArr = this.currentOptionArr
|
|
|
- }
|
|
|
- const { css } = currentOptionArr[index]
|
|
|
- let {
|
|
|
- width,
|
|
|
- height,
|
|
|
- left,
|
|
|
- top,
|
|
|
- borderRadius,
|
|
|
- borderWidth,
|
|
|
- borderColor,
|
|
|
- background,
|
|
|
- rotate,
|
|
|
- // align,
|
|
|
- shadow,
|
|
|
- mode,
|
|
|
- url
|
|
|
- } = css
|
|
|
- width = width / 1
|
|
|
- height = height / 1
|
|
|
- left = left / 1
|
|
|
- top = top / 1
|
|
|
- borderRadius = borderRadius / 1
|
|
|
- borderWidth = borderWidth / 1
|
|
|
- rotate = rotate / 1
|
|
|
- shadow = shadow
|
|
|
- .trim()
|
|
|
- .split(/\s+/)
|
|
|
- .join(' ')
|
|
|
- const Shape = await this.loadImageUrl(url)
|
|
|
- const imgWidth = Shape.width
|
|
|
- const imgHeight = Shape.height
|
|
|
- Shape.set({
|
|
|
- url,
|
|
|
- // align,
|
|
|
- mode,
|
|
|
- shadow,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center'
|
|
|
- })
|
|
|
- if (mode === 'scaleToFill') {
|
|
|
- Shape.set({
|
|
|
- width: imgWidth,
|
|
|
- height: imgHeight,
|
|
|
- scaleX: width / imgWidth,
|
|
|
- scaleY: height / imgHeight,
|
|
|
- oldScaleX: width / imgWidth,
|
|
|
- oldScaleY: height / imgHeight
|
|
|
- })
|
|
|
- Shape.clipPath = new fabric.Rect({
|
|
|
- width,
|
|
|
- height,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- rx: borderRadius,
|
|
|
- scaleX: imgWidth / width,
|
|
|
- scaleY: imgHeight / height
|
|
|
- })
|
|
|
- } else if (mode === 'auto') {
|
|
|
- // 忽略高度会自适应宽度,等比缩放图片
|
|
|
- Shape.set({
|
|
|
- width: imgWidth,
|
|
|
- height: imgHeight,
|
|
|
- scaleX: width / imgWidth,
|
|
|
- scaleY: width / imgWidth,
|
|
|
- oldScaleX: width / imgWidth,
|
|
|
- oldScaleY: height / imgHeight
|
|
|
- })
|
|
|
- Shape.clipPath = new fabric.Rect({
|
|
|
- width,
|
|
|
- height,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- rx: borderRadius,
|
|
|
- scaleX: imgWidth / width,
|
|
|
- scaleY: imgHeight / height
|
|
|
- })
|
|
|
- } else if (mode === 'aspectFill') {
|
|
|
- Shape.clipPath = new fabric.Rect({
|
|
|
- width: width / 1,
|
|
|
- height: height / 1,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- rx: borderRadius / 1
|
|
|
- })
|
|
|
- Shape.set({
|
|
|
- width,
|
|
|
- height
|
|
|
- })
|
|
|
- }
|
|
|
- const group = new fabric.Group([Shape], {
|
|
|
- left: left + width / 2 + borderWidth,
|
|
|
- top: top + height / 2 + borderWidth,
|
|
|
- width: width + borderWidth,
|
|
|
- height: height + borderWidth,
|
|
|
- rx: borderRadius / 1,
|
|
|
- strokeWidth: borderWidth / 1,
|
|
|
- stroke: borderColor,
|
|
|
- fill: background,
|
|
|
- angle: rotate,
|
|
|
- shadow,
|
|
|
- myshadow: shadow,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- mytype: 'image',
|
|
|
- mode,
|
|
|
- url,
|
|
|
- lockUniScaling: true // 只能等比缩放
|
|
|
- })
|
|
|
- // 添加边框
|
|
|
- group.add(
|
|
|
- new fabric.Rect({
|
|
|
- width: width + borderWidth,
|
|
|
- height: height + borderWidth,
|
|
|
- left: 0,
|
|
|
- top: 0,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- // padding,
|
|
|
- rx: borderRadius + borderWidth / 2,
|
|
|
- strokeWidth: borderWidth / 1,
|
|
|
- stroke: borderColor,
|
|
|
- fill: 'rgba(0,0,0,0)',
|
|
|
- shadow,
|
|
|
- selectable: false
|
|
|
- })
|
|
|
- )
|
|
|
- group.toObject = (function (toObject) {
|
|
|
- return function () {
|
|
|
- return fabric.util.object.extend(toObject.call(this), {
|
|
|
- mytype: 'image',
|
|
|
- mode,
|
|
|
- url,
|
|
|
- rx: borderRadius + borderWidth / 2,
|
|
|
- oldScaleX: width / imgWidth,
|
|
|
- oldScaleY: height / imgHeight,
|
|
|
- myshadow: shadow
|
|
|
- })
|
|
|
- }
|
|
|
- })(group.toObject)
|
|
|
- // console.log('group', group);
|
|
|
- return group
|
|
|
- }
|
|
|
-
|
|
|
- async addQrcodeObject (index, action) {
|
|
|
- let currentOptionArr
|
|
|
- if (action === 'update') {
|
|
|
- currentOptionArr = this.state.currentOptionArr
|
|
|
- } else {
|
|
|
- currentOptionArr = this.currentOptionArr
|
|
|
- }
|
|
|
- const { css } = currentOptionArr[index]
|
|
|
- let {
|
|
|
- width,
|
|
|
- left,
|
|
|
- top,
|
|
|
- color,
|
|
|
- borderRadius,
|
|
|
- // borderWidth,
|
|
|
- // borderColor,
|
|
|
- background,
|
|
|
- rotate,
|
|
|
- url
|
|
|
- // align,
|
|
|
- } = css
|
|
|
- width = width / 1
|
|
|
- left = left / 1 + width / 2
|
|
|
- top = top / 1 + width / 2
|
|
|
- rotate = rotate / 1
|
|
|
- const imgBase64 = jrQrcode.getQrBase64(url, {
|
|
|
- padding: 0, // 二维码四边空白(默认为10px)
|
|
|
- width: width / 1, // 二维码图片宽度(默认为256px)
|
|
|
- height: width / 1, // 二维码图片高度(默认为256px)
|
|
|
- correctLevel: QRErrorCorrectLevel.H, // 二维码容错level(默认为高)
|
|
|
- reverse: false, // 反色二维码,二维码颜色为上层容器的背景颜色
|
|
|
- background: background, // 二维码背景颜色(默认白色)
|
|
|
- foreground: color // 二维码颜色(默认黑色)
|
|
|
- })
|
|
|
- const Shape = await this.loadImageUrl(imgBase64)
|
|
|
- Shape.set({
|
|
|
- url,
|
|
|
- width: width / 1,
|
|
|
- height: width / 1,
|
|
|
- left,
|
|
|
- top,
|
|
|
- color,
|
|
|
- background,
|
|
|
- rx: borderRadius / 1,
|
|
|
- // strokeWidth: borderWidth / 1,
|
|
|
- // stroke: borderColor,
|
|
|
- // align,
|
|
|
- angle: rotate / 1,
|
|
|
- lockUniScaling: true, // 只能等比缩放
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- mytype: 'qrcode'
|
|
|
- })
|
|
|
- Shape.clipPath = new fabric.Rect({
|
|
|
- width,
|
|
|
- height: width / 1,
|
|
|
- originX: 'center',
|
|
|
- originY: 'center',
|
|
|
- rx: borderRadius,
|
|
|
- angle: rotate / 1
|
|
|
- })
|
|
|
- Shape.toObject = (function (toObject) {
|
|
|
- return function () {
|
|
|
- return fabric.util.object.extend(toObject.call(this), {
|
|
|
- mytype: 'qrcode',
|
|
|
- url,
|
|
|
- color,
|
|
|
- background,
|
|
|
- rx: borderRadius / 1
|
|
|
- })
|
|
|
- }
|
|
|
- })(Shape.toObject)
|
|
|
- return Shape
|
|
|
- }
|
|
|
-
|
|
|
- loadImageUrl = (imgUrl) => {
|
|
|
- return new Promise(resolve => {
|
|
|
- fabric.Image.fromURL(imgUrl, function (oImg) {
|
|
|
- resolve(oImg)
|
|
|
- })
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- changeActiveObjectValue = () => {
|
|
|
- const type = this.activeObject.mytype
|
|
|
- if (!type) return
|
|
|
- // this.setState({
|
|
|
- // visible: true
|
|
|
- // })
|
|
|
- const item2 = this.activeObject
|
|
|
- const width = `${(item2.width - item2.strokeWidth) * item2.scaleX}`
|
|
|
- const height = `${(item2.height - item2.strokeWidth) * item2.scaleY}`
|
|
|
- /* let left = `${(item2.left / item2.scaleY - (item2.width - item2.strokeWidth) / 2 - item2.strokeWidth).toFixed(2)}`;
|
|
|
- let top = `${(item2.top / item2.scaleY - (item2.height - item2.strokeWidth) / 2 - item2.strokeWidth).toFixed(2)}`; */
|
|
|
- const left = `${(item2.left - width / 2).toFixed(2)}`
|
|
|
- const top = `${(item2.top - height / 2).toFixed(2)}`
|
|
|
-
|
|
|
- let css = {
|
|
|
- width,
|
|
|
- height,
|
|
|
- left,
|
|
|
- top,
|
|
|
- color: `${item2.color}`,
|
|
|
- background: `${item2.fill}`,
|
|
|
- rotate: `${item2.angle}`,
|
|
|
- borderRadius: `${item2.rx * item2.scaleY}`,
|
|
|
- borderWidth: `${item2.strokeWidth * item2.scaleY}`,
|
|
|
- borderColor: `${item2.stroke}`,
|
|
|
- shadow: `${item2.myshadow}`
|
|
|
- }
|
|
|
- let index = ''
|
|
|
- switch (type) {
|
|
|
- case 'textGroup':
|
|
|
- index = 1
|
|
|
- item2._objects.forEach(ele => {
|
|
|
- // const css2 = {
|
|
|
- // text: '',
|
|
|
- // width,
|
|
|
- // maxLines: '',
|
|
|
- // lineHeight: '',
|
|
|
- // left,
|
|
|
- // top,
|
|
|
- // color: `${item2.color}`,
|
|
|
- // background: `${item2.fill}`,
|
|
|
- // fontSize: '',
|
|
|
- // fontWeight: '',
|
|
|
- // textDecoration: '',
|
|
|
- // rotate: `${item2.angle}`,
|
|
|
- // // padding: 0,
|
|
|
- // borderRadius: `${item2.rx * item2.scaleY}`,
|
|
|
- // borderWidth: `${item2.strokeWidth * item2.scaleY}`,
|
|
|
- // borderColor: `${item2.stroke}`,
|
|
|
- // shadow: `${item2.shadow}`,
|
|
|
- // textStyle: '',
|
|
|
- // textAlign: '',
|
|
|
- // fontFamily: ''
|
|
|
- // }
|
|
|
- // if (ele.type === 'rect') {
|
|
|
- // } else {
|
|
|
-
|
|
|
- // }
|
|
|
- css = {
|
|
|
- // text: '',
|
|
|
- width,
|
|
|
- // maxLines: '',
|
|
|
- // lineHeight: '',
|
|
|
- left,
|
|
|
- top,
|
|
|
- // color: `${item2.color}`,
|
|
|
- background: `${item2.fill}`,
|
|
|
- // fontSize: '',
|
|
|
- // fontWeight: '',
|
|
|
- // textDecoration: '',
|
|
|
- rotate: `${item2.angle}`,
|
|
|
- // padding: 0,
|
|
|
- borderRadius: `${item2.rx * item2.scaleY}`,
|
|
|
- borderWidth: `${item2.strokeWidth * item2.scaleY}`,
|
|
|
- borderColor: `${item2.stroke}`,
|
|
|
- // shadow: `${item2.shadow}`,
|
|
|
- // textStyle: '',
|
|
|
- // textAlign: '',
|
|
|
- // fontFamily: '',
|
|
|
- text: `${item2.oldText}`,
|
|
|
- maxLines: `${ele.maxLines}`,
|
|
|
- lineHeight: `${ele.lineHeight}`,
|
|
|
- color: ele.fill,
|
|
|
- // padding: `${ele.padding}`,
|
|
|
- fontSize: `${ele.fontSize}`,
|
|
|
- fontWeight: `${ele.fontWeight}`,
|
|
|
- textStyle: `${ele.textStyle}`,
|
|
|
- textDecoration: `${ele.textDecoration === 'linethrough' ? 'line-through' : ele.textDecoration}`,
|
|
|
- fontFamily: `${ele.fontFamily}`,
|
|
|
- textAlign: `${ele.textAlign}`,
|
|
|
- shadow: `${item2.myshadow}`
|
|
|
- }
|
|
|
- })
|
|
|
- break
|
|
|
- case 'image':
|
|
|
- index = 2
|
|
|
- delete css.color
|
|
|
- delete css.background
|
|
|
- css = {
|
|
|
- url: item2.url,
|
|
|
- ...css,
|
|
|
- mode: `${item2.mode}`,
|
|
|
- shadow: `${item2.myshadow}`
|
|
|
- }
|
|
|
- break
|
|
|
- case 'qrcode':
|
|
|
- index = 3
|
|
|
- delete css.height
|
|
|
- delete css.borderWidth
|
|
|
- delete css.borderColor
|
|
|
- delete css.shadow
|
|
|
- css = {
|
|
|
- url: item2.url,
|
|
|
- ...css,
|
|
|
- color: item2.color,
|
|
|
- background: item2.background
|
|
|
- }
|
|
|
- break
|
|
|
- default:
|
|
|
- break
|
|
|
- }
|
|
|
- const currentOptionArr = cloneDeep(this.state.currentOptionArr)
|
|
|
- currentOptionArr[index].css = css
|
|
|
- this.setState({
|
|
|
- currentOptionArr
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- handleChange = (index, key, value) => {
|
|
|
- const currentOptionArr = cloneDeep(this.state.currentOptionArr)
|
|
|
- currentOptionArr[index].css[key] = value
|
|
|
- this.setState(
|
|
|
- {
|
|
|
- currentOptionArr
|
|
|
- },
|
|
|
- () => {
|
|
|
- this.updateObject()
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- async updateObject () {
|
|
|
- const type = this.activeObject.mytype
|
|
|
- this.canvas_sprite.remove(this.activeObject)
|
|
|
- switch (type) {
|
|
|
- case 'textGroup':
|
|
|
- await this.addShape(1, 'update')
|
|
|
- break
|
|
|
- case 'image':
|
|
|
- await this.addShape(2, 'update')
|
|
|
- break
|
|
|
- case 'qrcode':
|
|
|
- await this.addShape(3, 'update')
|
|
|
- break
|
|
|
- default:
|
|
|
- break
|
|
|
- }
|
|
|
- this.canvas_sprite.renderAll()
|
|
|
- }
|
|
|
-
|
|
|
- addEventListener = () => {
|
|
|
- const throttlechangeActiveObjectValue = throttle(this.changeActiveObjectValue, 100)
|
|
|
- this.canvas_sprite.on('object:moving', (e) => {
|
|
|
- console.log('object:moving')
|
|
|
- var obj = e.target
|
|
|
- // if object is too big ignore
|
|
|
- if (obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width) {
|
|
|
- return
|
|
|
- }
|
|
|
- obj.setCoords()
|
|
|
- // top-left corner
|
|
|
- if (obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0) {
|
|
|
- obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top)
|
|
|
- obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left)
|
|
|
- }
|
|
|
- // bot-right corner
|
|
|
- if (
|
|
|
- obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height ||
|
|
|
- obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width
|
|
|
- ) {
|
|
|
- obj.top = Math.min(
|
|
|
- obj.top,
|
|
|
- obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top
|
|
|
- )
|
|
|
- obj.left = Math.min(
|
|
|
- obj.left,
|
|
|
- obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- throttlechangeActiveObjectValue()
|
|
|
- })
|
|
|
- this.canvas_sprite.on('object:scaling', (e) => {
|
|
|
- console.log('object:scaling')
|
|
|
- var obj = e.target
|
|
|
- // if object is too big ignore
|
|
|
- if (obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width) {
|
|
|
- return
|
|
|
- }
|
|
|
- obj.setCoords()
|
|
|
- // top-left corner
|
|
|
- if (obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0) {
|
|
|
- obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top)
|
|
|
- obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left)
|
|
|
- }
|
|
|
- // bot-right corner
|
|
|
- if (
|
|
|
- obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height ||
|
|
|
- obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width
|
|
|
- ) {
|
|
|
- obj.top = Math.min(
|
|
|
- obj.top,
|
|
|
- obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top
|
|
|
- )
|
|
|
- obj.left = Math.min(
|
|
|
- obj.left,
|
|
|
- obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left
|
|
|
- )
|
|
|
- }
|
|
|
- throttlechangeActiveObjectValue()
|
|
|
- })
|
|
|
- this.canvas_sprite.on('mouse:down', (e) => {
|
|
|
- console.log('mouse:down')
|
|
|
- if (e.target) {
|
|
|
- this.activeObject = e.target
|
|
|
- this.changeActiveObjectValue()
|
|
|
- }
|
|
|
- })
|
|
|
- // 解决放大缩小元素位置不对的问题
|
|
|
- this.canvas_sprite.on('object:scaled', (e) => {
|
|
|
- console.log('object:scaled')
|
|
|
- if (e.target) {
|
|
|
- this.activeObject = e.target
|
|
|
- this.updateObject()
|
|
|
- }
|
|
|
- })
|
|
|
- this.canvas_sprite.on('object:modified', () => {
|
|
|
- console.log('object:modified')
|
|
|
- this.updateCanvasState()
|
|
|
- })
|
|
|
-
|
|
|
- this.canvas_sprite.on('object:added', () => {
|
|
|
- console.log('object:added')
|
|
|
- this.updateCanvasState()
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- updateCanvasState = () => {
|
|
|
- const canvas_sprite = this.canvas_sprite
|
|
|
- if (_config.undoStatus === false && _config.redoStatus === false) {
|
|
|
- var jsonData = canvas_sprite.toJSON()
|
|
|
- var canvasAsJson = JSON.stringify(jsonData)
|
|
|
- if (_config.currentStateIndex < _config.canvasState.length - 1) {
|
|
|
- var indexToBeInserted = _config.currentStateIndex + 1
|
|
|
- _config.canvasState[indexToBeInserted] = canvasAsJson
|
|
|
- var numberOfElementsToRetain = indexToBeInserted + 1
|
|
|
- _config.canvasState = _config.canvasState.splice(0, numberOfElementsToRetain)
|
|
|
- } else {
|
|
|
- _config.canvasState.push(canvasAsJson)
|
|
|
- }
|
|
|
- _config.currentStateIndex = _config.canvasState.length - 1
|
|
|
- if (_config.currentStateIndex === _config.canvasState.length - 1 && _config.currentStateIndex !== -1) {
|
|
|
- this.setState({
|
|
|
- redoButtonStatus: 'disabled'
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- save = () => {
|
|
|
- }
|
|
|
-
|
|
|
- render () {
|
|
|
- const canvasOption = this.currentOptionArr[0].css
|
|
|
- return (
|
|
|
- <React.Fragment>
|
|
|
- <div className={styles.header}><span>模版:{this.state.name}</span><span>保存于00:00:00 <Button>预览</Button><Button type="primary" onClick={this.save}>保存</Button></span></div>
|
|
|
- <div className={styles.content}>
|
|
|
- <div className={styles.left}>
|
|
|
- <div className={styles.box}>
|
|
|
- <h2>背景</h2>
|
|
|
- <Form layout="vertical">
|
|
|
- <Form.Item label="宽度" >
|
|
|
- <Input defaultValue={canvasOption.width} onChange={(e) => {
|
|
|
- this.currentOptionArr[0].css.width = e.target.value
|
|
|
- this.canvas_sprite.setWidth(e.target.value)
|
|
|
- }}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="高度">
|
|
|
- <Input defaultValue={canvasOption.height} onChange={(e) => {
|
|
|
- this.currentOptionArr[0].css.height = e.target.value
|
|
|
- this.canvas_sprite.setHeight(e.target.value)
|
|
|
- }}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="背景色">
|
|
|
- <Input defaultValue={canvasOption.background} onChange={(e) => {
|
|
|
- this.currentOptionArr[0].css.background = e.target.value
|
|
|
- this.canvas_sprite.setBackgroundColor(e.target.value)
|
|
|
- this.canvas_sprite.renderAll()
|
|
|
- }}/>
|
|
|
- </Form.Item>
|
|
|
- </Form>
|
|
|
- </div>
|
|
|
- <div className={styles.box}>
|
|
|
- <h2>元素</h2>
|
|
|
- <ul className={styles.elem}>
|
|
|
- <li onClick={() => this.addShape(1)}>文字</li>
|
|
|
- <li onClick={() => this.addShape(2)}>图片</li>
|
|
|
- <li onClick={() => this.addShape(3)}>二维码</li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- <div className={styles.box}>
|
|
|
- <h2>图层列表</h2>
|
|
|
- <ul className={styles.layer}>
|
|
|
- <li className={styles.selected}>xxxx</li>
|
|
|
- <li>xxxx</li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div className={styles.middle}>
|
|
|
- <canvas id="canvas"></canvas>
|
|
|
- </div>
|
|
|
- <div className={styles.right}>
|
|
|
- {
|
|
|
- this.activeObject.mytype === 'textGroup' && (
|
|
|
- <div className={styles.box}>
|
|
|
- <h2>文字</h2>
|
|
|
- <Form layout="vertical">
|
|
|
- <Form.Item label="内容">
|
|
|
- <Input.TextArea rows={3} defaultValue={this.state.currentOptionArr[1].css.text} onChange={(e) => this.handleChange(1, 'text', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <Form.Item label="字体" style={{ width: 150 }}>
|
|
|
- <Select defaultValue={this.state.currentOptionArr[1].css.fontFamily} style={{ width: 145 }} onChange={(value) => this.handleChange(1, 'fontFamily', value)}>
|
|
|
- <Option value="msyh">msyh</Option>
|
|
|
- <Option value="msyhbd">msyhbd</Option>
|
|
|
- <Option value="NotoSerifCJK">NotoSerifCJK</Option>
|
|
|
- <Option value="PingFang">PingFang</Option>
|
|
|
- <Option value="pingfang-sc-semibold">pingfang-sc-semibold</Option>
|
|
|
- <Option value="pingfang_jiancu">pingfang_jiancu</Option>
|
|
|
- <Option value="pingfang_jianxi">pingfang_jianxi</Option>
|
|
|
- <Option value="pingfang_regular">pingfang_regular</Option>
|
|
|
- <Option value="PingFang_SC">PingFang_SC</Option>
|
|
|
- <Option value="pingfang_sc_medium">pingfang_sc_medium</Option>
|
|
|
- <Option value="pingfang_sc_semibold">pingfang_sc_semibold</Option>
|
|
|
- <Option value="simhei">simhei</Option>
|
|
|
- <Option value="SourceHanSansCN-Normal">SourceHanSansCN-Normal</Option>
|
|
|
- <Option value="喜鹊招牌体">喜鹊招牌体</Option>
|
|
|
- <Option value="喜鹊聚珍体regular">喜鹊聚珍体regular</Option>
|
|
|
- <Option value="思源宋体">思源宋体</Option>
|
|
|
- <Option value="方正正粗黑简体">方正正粗黑简体</Option>
|
|
|
- </Select>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="字体大小" style={{ flex: 1 }}>
|
|
|
- <InputNumber style={{ width: 80 }} min={12} max={100} defaultValue={this.state.currentOptionArr[1].css.fontSize} onChange={(value) => this.handleChange(1, 'fontSize', value)} />
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="字体颜色" style={{ flex: 1 }}>
|
|
|
- <RcColorPicker
|
|
|
- color={this.state.currentOptionArr[1].css.color}
|
|
|
- onChange={({ color }) => this.handleChange(1, 'color', color)}
|
|
|
- >
|
|
|
- <Input style={{ width: 80 }} />
|
|
|
- </RcColorPicker>
|
|
|
- </Form.Item>
|
|
|
- </div>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <Form.Item label="X偏移" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[1].css.left} onChange={(e) => this.handleChange(1, 'left', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="Y偏移" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[1].css.top} onChange={(e) => this.handleChange(1, 'top', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- </div>
|
|
|
- <Form.Item label="文字宽度">
|
|
|
- <Input defaultValue={this.state.currentOptionArr[1].css.width} onChange={(e) => this.handleChange(1, 'width', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="文字行数">
|
|
|
- <Input defaultValue={this.state.currentOptionArr[1].css.maxLines} onChange={(e) => this.handleChange(1, 'maxLines', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="X居中">
|
|
|
- <Select defaultValue={this.state.currentOptionArr[1].css.textAlign} style={{ width: 150 }} onChange={(value) => this.handleChange(1, 'textAlign', value)}>
|
|
|
- <Option value="left">left</Option>
|
|
|
- <Option value="center">center</Option>
|
|
|
- <Option value="right">right</Option>
|
|
|
- </Select>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="文字垂直">
|
|
|
- <Input/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="不透明">
|
|
|
- <Slider
|
|
|
- min={1}
|
|
|
- max={20}
|
|
|
- onChange={(value) => console.log(value)}
|
|
|
- value={10}
|
|
|
- />
|
|
|
- </Form.Item>
|
|
|
- </Form>
|
|
|
- </div>
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- this.activeObject.mytype === 'qrcode' && (
|
|
|
- <div className={styles.box}>
|
|
|
- <h2>二维码</h2>
|
|
|
- <Form layout="vertical">
|
|
|
- <Form.Item label="内容">
|
|
|
- <Input defaultValue={this.state.currentOptionArr[3].css.url} onChange={(e) => this.handleChange(3, 'url', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <Form.Item label="X偏移" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[3].css.left} onChange={(e) => this.handleChange(3, 'left', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="Y偏移" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[3].css.top} onChange={(e) => this.handleChange(3, 'top', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- </div>
|
|
|
- <Form.Item label="LOGO">
|
|
|
- <Switch defaultChecked onChange={(checked) => console.log(checked)} checkedChildren="开" unCheckedChildren="关" />
|
|
|
- <Select defaultValue="wpt" style={{ width: 120, marginLeft: '5px' }}>
|
|
|
- <Option value="wpt">微拍堂</Option>
|
|
|
- <Option value="youjiang"></Option>
|
|
|
- </Select>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="大小">
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[3].css.width} onChange={(e) => this.handleChange(3, 'width', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- </Form>
|
|
|
- </div>
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- this.activeObject.mytype === 'image' && (
|
|
|
- <div className={styles.box}>
|
|
|
- <h2>图片</h2>
|
|
|
- <Form layout="vertical">
|
|
|
- <Form.Item label="路径">
|
|
|
- <Input/>
|
|
|
- </Form.Item>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <Form.Item label="宽" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[2].css.width} onChange={(e) => this.handleChange(2, 'width', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="高" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[2].css.height} onChange={(e) => this.handleChange(2, 'height', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- </div>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <Form.Item label="X偏移" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[2].css.left} onChange={(e) => this.handleChange(2, 'left', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="Y偏移" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[2].css.top} onChange={(e) => this.handleChange(2, 'top', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- </div>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <Form.Item label="圆形" style={{ flex: 1 }}>
|
|
|
- <Switch defaultChecked onChange={(checked) => {
|
|
|
- if (checked) {
|
|
|
- this.handleChange(2, 'borderRadius', 1000)
|
|
|
- } else {
|
|
|
- this.handleChange(2, 'borderRadius', 0)
|
|
|
- }
|
|
|
- }} checkedChildren="开" unCheckedChildren="关" />
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="圆角" style={{ flex: 1 }}>
|
|
|
- <Input style={{ width: 100 }} defaultValue={this.state.currentOptionArr[2].css.borderRadius} onChange={(e) => this.handleChange(2, 'borderRadius', e.target.value)}/>
|
|
|
- </Form.Item>
|
|
|
- </div>
|
|
|
- <Form.Item label="高斯模糊">
|
|
|
- <Input/>
|
|
|
- </Form.Item>
|
|
|
- <Form.Item label="图片裁剪选项">
|
|
|
- <Input/>
|
|
|
- </Form.Item>
|
|
|
- </Form>
|
|
|
- </div>
|
|
|
- )
|
|
|
- }
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </React.Fragment>
|
|
|
- )
|
|
|
- }
|
|
|
-}
|