| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- import fabric from '../../js/fabric-adapter'
- import del from './images/sc.svg'
- import rotate from './images/xz.svg'
- import symmetry from './images/jx.svg'
- import jia from './images/jia.svg'
- import jian from './images/jian.svg'
- import pingpu from './images/pingpu.svg'
- import { uploadBaseImg } from '@/apis/other'
- export default {
- data() {
- return {
- action: 'select',
- drawWidth: 20,
- eraseOpacity:1, //橡皮擦 透明度
- min: 2,
- max: 80,
- color: '#ffffff',
- //撤销
- canvasState: [],//操作记录
- stateIndex: -1,
- canUndoState:false,
- //画笔在层上操作
- group:null,
- arr:[],
- }
- },
- props: {
- toolConfig: {
- type: Array,
- default: () => ['select','color','draw','eraser','empty','undo']
- },
- controlToolConfig: {
- type: Array,
- default: () => ['del','rotate','symmetry']
- },
- defaultColor:{ //画笔默认颜色
- type: String,
- default:'#ffffff'
- }
- },
- computed: {
- //是否可以撤销
- canUndo() {
- return this.stateIndex >= 0;
- },
- },
- watch: {
- action(val,old){
- if(old==='color' && this.showStraw){
- this.hideColorXg()
- }
- },
- drawWidth() {
- if (this.fcanvas){
- this.fcanvas.freeDrawingBrush.width = this.drawWidth
- this.setCursor()
- }
- },
- color() {
- if (this.fcanvas) {
- this.fcanvas.freeDrawingBrush.color = this.color
- this.toDraw()
- }
- },
- eraseOpacity(){
- if (this.fcanvas) {
- this.fcanvas.freeDrawingBrush.opacity = this.eraseOpacity
- this.setCursor()
- }
- }
- },
- created() {
- this.color = this.defaultColor
- },
- activated() {
- //设置控件 添加镜像删除等icon
- this.setControlTool();
- },
- mounted() {
- //设置控件 添加镜像删除等icon
- this.setControlTool();
- },
- methods: {
- actionInit(){
- //监听撤销
- if(this.toolConfig.includes('undo')){
- this.setUndo()
- }
- //修改画笔 每次在此图层上操作
- if(this.toolConfig.includes('draw')){
- this.fcanvas.on('path:created', (opt)=>{
- if(this.action === 'erase') {
- this.selected.map(item=>{
- this.updateLayerCover(item)
- })
- this.getLayers()
- return;
- }
- if(this.action !== 'draw') return;
- let object = this.fcanvas.getObjects()
- let path = object[object.length-1]
- let selected = []
- let name = 'pencli'
- let sort = null
- let id = null
- if(this.selected.length === 1){
- name = this.selected[0].name
- sort = this.selected[0].sort
- id = this.selected[0].id
- selected = this.selected
- }else{
- id = this.getNextLayersId()
- sort = this.getNextLayersSort()
- }
- selected.push(path)
- var sel = new fabric.ActiveSelection(selected, {
- canvas: this.fcanvas,
- });
- this.fcanvas.setActiveObject(sel);
- // this.canUndoState = true;
- const group = this.fcanvas.getActiveObject().toGroup();
- group.setControlsVisibility({
- 'mtr':false,
- })
- group.set({
- name,
- sort,
- id,
- erasable:false,
- })
- group.moveTo(sort)
- this.fcanvas.discardActiveObject()
- this.selected = [group]
- // this.updateCanvasState();
- this.getLayers()
- })
- }
- this.fcanvas.on('mouse:down', (opt)=>{
- if(this.action !== 'erase') return;
- this.toEraseInLayer();
- })
- //设置默认笔画
- setTimeout(() => {
- this.fcanvas.freeDrawingBrush.width = this.drawWidth
- this.fcanvas.freeDrawingBrush.color = this.color
- }, 100)
- },
- // 新增图片图层
- async addMaps(url, options = {
- left:50,
- bottom:50,
- }, ) {
- return new Promise((resolve, reject) => {
- if(!this.fcanvas.add){
- reject(false);
- }
- fabric.Image.fromURL(url, (img) => {
- let sort = options.sort || this.getNextLayersSort()
- let erasable = options.erasable === false ? false : true
- let selectable = options.selectable === false ? false : true
- let id = this.getNextLayersId()
- this.fcanvas.discardActiveObject()
- img.setControlsVisibility({
- 'mtr':false,
- })
- this.fcanvas.add(img.set({
- name:"image",
- ...options,
- sort,
- id,
- erasable:erasable,
- }))
- img.moveTo(sort)
- if(img.getScaledWidth() > options.maxWidth){
- let zoom = this.fcanvas.getZoom()
- img.scaleToWidth(options.maxWidth * this.fcanvas.getZoom())
- }
- if(img.getScaledHeight() > options.maxHeight){
- img.scaleToHeight(options.maxHeight * this.fcanvas.getZoom())
- }
- if(selectable) this.fcanvas.setActiveObject(img)
- this.toSelect()
- this.getLayers()
- if(selectable) this.select(img)
- this.fcanvas.renderAll();
- console.log(img)
- resolve(img)
- }, {
- crossOrigin: 'anonymous',
- })
- })
- },
- //新增 svg
- addSvg(url, options = {
- left:50,
- top:50,
- width:200,
- height:200
- },){
- if(!this.fcanvas.add){
- return;
- }
- fabric.loadSVGFromURL(url, (objects,options) => {
- const svg = fabric.util.groupSVGElements(objects, options);
- let sort = options.sort || this.getNextLayersSort()
- let erasable = options.erasable || false
- let id = this.getNextLayersId()
- this.fcanvas.discardActiveObject()
- svg.setControlsVisibility({
- 'mtr':false,
- })
- this.fcanvas.add(svg.set({
- ...options,
- name:"svg",
- sort,
- id,
- erasable:erasable,
- }))
- svg.moveTo(sort)
- this.fcanvas.setActiveObject(svg)
- this.toSelect()
- this.getLayers()
- this.select(svg)
- this.fcanvas.renderAll();
- })
- },
- //新增文字图层
- addText( options = {
- left:50,
- top:50,
- },){
- if(!this.fcanvas.add){
- return;
- }
- const textbox = new fabric.Textbox('', {
- left: 50,
- top: 50,
- width: 150,
- fontSize: 20
- });
- let sort = options.sort || this.getNextLayersSort()
- let erasable = options.erasable || false
- let id = this.getNextLayersId()
- this.fcanvas.discardActiveObject()
- this.fcanvas.add(textbox.set({
- ...options,
- name:"text",
- sort,
- id,
- erasable:erasable,
- }))
- textbox.moveTo(sort)
- this.fcanvas.setActiveObject(textbox)
- this.toSelect()
- this.getLayers()
- this.select(textbox)
- textbox.enterEditing();
- this.fcanvas.renderAll();
- },
- // 选择
- toSelect() {
- this.action = 'select'
- this.fcanvas.isDrawingMode = false
- },
- async setCursor(){
- if(!['draw','erase'].includes(this.action)) return
- let fcanvasZoom = this.fcanvas.getZoom()
- let deviceZoom = detectZoom.device()
- let zoom = fcanvasZoom/deviceZoom;
- const canvas = document.createElement('canvas')
- const fcanvas = new fabric.Canvas(canvas, {
- containerClass:"fcanvas",
- // 元素对象被选中时保持在当前z轴,不会跳到最顶层
- preserveObjectStacking:true,
- width: this.drawWidth*zoom,
- height: this.drawWidth*zoom,
- })
- const color = this.action === 'draw' ? this.color : '#ffffff';
- var rect = new fabric.Circle({
- radius: (this.drawWidth*zoom)/2,
- fill:color,
- left: 0,
- opacity: 1,
- strokeWidth: 0,
- strokeUniform: true
- });
- fcanvas.add(rect);
- const base64 = await fcanvas.toDataURL({
- format: 'png',
- enableRetinaScaling: true
- })
- this.fcanvas.freeDrawingCursor = `url( ${base64} ) ${(this.drawWidth*fcanvasZoom)/2} ${(this.drawWidth*fcanvasZoom)/2}, auto`;
- },
- // 画笔
- toDraw() {
- this.action = 'draw'
- this.fcanvas.freeDrawingBrush = new fabric.PencilBrush(this.fcanvas)
- this.fcanvas.freeDrawingBrush.canvas.selectable = false;
- this.setFreeDrawingBrushStyle()
- this.setCursor()
- this.fcanvas.renderAll();
- },
- // 橡皮擦
- toErase() {
- this.action = 'erase'
- console.log(this.fcanvas.getObjects())
- this.fcanvas.freeDrawingBrush = new fabric.EraserBrush(this.fcanvas)
- this.toEraseInLayer();
- this.setFreeDrawingBrushStyle()
- this.setCursor()
- },
- //拖拽
- toDrag(){
- this.action = 'drag'
- this.fcanvas.isDrawingMode = false
- this.fcanvas.hoverCursor = 'grab'
- },
- //设置橡皮擦作用图层
- toEraseInLayer(){
- this.fcanvas.getObjects().map(item=>{
- if(this.selectedIds.includes(item.id)){
- item.set({
- erasable:true,
- })
- }else{
- item.set({
- erasable:false,
- })
- }
- })
- },
- // 清空
- setEmpty() {
- this.fcanvas.remove.apply(this.fcanvas, this.fcanvas.getObjects())
- this.getLayers()
- this.undoAfterSelectLayers();
- },
- //合并组
- toGroup(){
- this.fcanvas.getActiveObject().toGroup();
- this.fcanvas.requestRenderAll();
- },
- // 设置画笔样式
- setFreeDrawingBrushStyle() {
- if (!this.fcanvas) return
- this.fcanvas.freeDrawingBrush.width = this.drawWidth
- this.fcanvas.freeDrawingBrush.color = this.color
- this.fcanvas.freeDrawingBrush.opacity = this.eraseOpacity
- this.fcanvas.isDrawingMode = true
- },
- //设置控件
- setControlTool(){
- let self = this;
- //修改 controls 默认样式
- fabric.Object.prototype.transparentCorners = false;
- fabric.Object.prototype.cornerColor = '#333333';
- fabric.Object.prototype.cornerStrokeColor = '#333333';
- fabric.Object.prototype.borderColor = '#000000';
- fabric.Object.prototype.cornerStyle = 'circle';
- fabric.Textbox.prototype.transparentCorners = false;
- fabric.Textbox.prototype.cornerColor = '#333333';
- fabric.Textbox.prototype.cornerStrokeColor = '#333333';
- fabric.Textbox.prototype.borderColor = '#000000';
- fabric.Textbox.prototype.cornerStyle = 'circle';
- //删除控件, 每次 重新计算
- let controlToolConfigFun = {
- del:'deleteControl',
- rotate:'rotateControl',
- tile:"tileControl",
- tileIconjian:"tileIconjianControl",
- symmetry:'symmetryControl',
- }
- Object.keys(controlToolConfigFun).map(item=>{
- delete fabric.Object.prototype.controls[controlToolConfigFun[item]]
- delete fabric.Textbox.prototype.controls[controlToolConfigFun[item]]
- })
- delete fabric.Object.prototype.controls.tileIconppControl
- delete fabric.Textbox.prototype.controls.tileIconppControl
- if(this.controlToolConfig.includes('del')) setToolDel()
- if(this.controlToolConfig.includes('rotate')) setToolRotate()
- if(this.controlToolConfig.includes('symmetry')) setToolSymmetry()
- if(this.controlToolConfig.includes('tile')) setTooTile()
- // 增加图标
- function renderIcon(icon) {
- return function renderIcon(ctx, left, top, styleOverride, fabricObject) {
- var size = this.cornerSize
- ctx.save()
- ctx.translate(left, top)
- ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle))
- ctx.drawImage(icon, -size / 2, -size / 2, size, size)
- ctx.restore()
- }
- }
- // 删除
- function setToolDel() {
- var icon = document.createElement('img')
- icon.src = del
- icon.title = '删除'
- fabric.Object.prototype.controls.deleteControl = new fabric.Control({
- x: -0.5,
- y: -0.5,
- offsetY: -25,
- cursorStyle:"Pointer",
- mouseUpHandler: deleteObject,
- render: renderIcon(icon),
- cornerSize: 20
- })
- fabric.Textbox.prototype.controls.deleteControl = fabric.Object.prototype.controls.deleteControl
- function deleteObject(eventData, transform) {
- try {
- var target = transform.target
- var canvas = target.canvas
- if(target.delable === false){
- self.$message.warning('该元素无法被删除')
- return;
- }
- canvas.remove(target)
- if(target.hasOwnProperty('_objects') && target.name !== 'svg'){
- target._objects.map(item=>{
- if(item.delable !== false){
- canvas.remove(item)
- }
- })
- canvas.discardActiveObject()
- }
- canvas.requestRenderAll()
- self.getLayers()
- self.undoAfterSelectLayers();
- }catch (e) {
- console.log(e)
- }
- }
- }
- // 旋转
- function setToolRotate() {
- var icon = document.createElement('img')
- icon.src = rotate
- icon.title = '旋转'
- fabric.Object.prototype.controls.rotateControl = new fabric.Control({
- x: 0,
- y: -0.5,
- actionHandler: fabric.controlsUtils.rotationWithSnapping,
- cursorStyleHandler: fabric.controlsUtils.rotationStyleHandler,
- offsetY: -40,
- withConnection: true,
- actionName: 'rotate',
- // 渲染图标
- cursorStyle:"Pointer",
- render: renderIcon(icon),
- // 设置控制点大小
- cornerSize: 20
- })
- fabric.Textbox.prototype.controls.rotateControl = fabric.Object.prototype.controls.rotateControl
- }
- // 镜像
- function setToolSymmetry() {
- let icon = document.createElement('img')
- icon.src = symmetry
- icon.title = '镜像'
- fabric.Object.prototype.controls.symmetryControl = new fabric.Control({
- x: 0.5,
- y: -0.5,
- offsetY: -25,
- offsetX: 0,
- cursorStyle:"Pointer",
- mouseUpHandler: deleteObject,
- render: renderIcon(icon),
- cornerSize: 20
- })
- fabric.Textbox.prototype.controls.symmetryControl = fabric.Object.prototype.controls.symmetryControl
- function deleteObject(eventData, transform) {
- var target = transform.target
- target.set({
- flipX:!target.flipX,
- })
- var canvas = target.canvas
- self.updateCanvasState()
- canvas.requestRenderAll()
- }
- }
- // 平铺
- function setTooTile() {
- let iconpp = document.createElement('img')
- iconpp.src = pingpu
- iconpp.title = '平铺'
- fabric.Object.prototype.controls.tileIconppControl = new fabric.Control({
- x: -0.5,
- y: -0.5,
- offsetX: 30,
- offsetY: -25,
- cursorStyle:"default",
- render: renderIcon(iconpp),
- cornerSize: 60
- })
- let icon = document.createElement('img')
- icon.src = jia
- icon.title = '平铺加'
- fabric.Object.prototype.controls.tileControl = new fabric.Control({
- x: -0.5,
- y: -0.5,
- offsetX: 60,
- offsetY: -25,
- cursorStyle:"Pointer",
- mouseUpHandler: tileObject,
- render: renderIcon(icon),
- cornerSize: 20
- })
- let iconjian = document.createElement('img')
- iconjian.src = jian
- iconjian.title = '平铺减'
- fabric.Object.prototype.controls.tileIconjianControl = new fabric.Control({
- x: -0.5,
- y: -0.5,
- offsetX: 0,
- offsetY: -25,
- cursorStyle:"Pointer",
- mouseUpHandler: tileObject,
- render: renderIcon(iconjian),
- cornerSize: 20
- })
- async function tileObject(eventData, transform) {
- var target = transform.target
- if(target.name !== 'image' || !target.tile ) return;
- if(target.tile.src){
- let multiple = target.tile.multiple + 1
- if(transform.corner === 'tileIconjianControl'){
- if(target.tile.multiple === 1){
- self.$message.error('无法在放大/缩小了')
- return
- }
- multiple = Math.max(target.tile.multiple - 1,1)
- }else{
- if(target.tile.multiple === 20){
- self.$message.error('无法在放大/缩小了')
- return
- }
- }
- let thisOssOptios = {
- ...target.tile.ossOptios,
- w:parseInt((target.tile.ossOptios.w)/multiple),
- h:parseInt((target.tile.ossOptios.h)/multiple)
- }
- let multipleImgSrc = self.$appfun.ossResize(target.tile.src,thisOssOptios)
- const image = await self.$appfun.loadingImg(multipleImgSrc)
- const canvas = document.createElement('canvas')
- canvas.width = target.width
- canvas.height = target.height
- const ctx = canvas.getContext('2d')
- var pat = ctx.createPattern(image,"repeat");
- ctx.rect(0,0,canvas.width,canvas.height);
- ctx.fillStyle = pat;
- ctx.fill();
- const url = canvas.toDataURL('image/png')
- target.setSrc(url,()=>{
- target.tile.multiple = multiple
- self.updateCanvasState()
- target.canvas.requestRenderAll()
- })
- }
- }
- }
- },
- //撤销
- historyState(index){
- if (this.canUndo) {
- this.canUndoState = true;
- this.fcanvas.loadFromJSON(this.canvasState[index], () => {
- this.fcanvas.renderAll();
- this.stateIndex = index;
- this.canvasState.splice(this.stateIndex+1);
- this.getLayers()
- this.undoAfterSelectLayers();
- this.canUndoState = false;
- });
- }
- },
- setUndo(){
- //监听撤销
- this.fcanvas.on("object:modified", ()=>{
- this.updateCanvasState()
- });
- },
- updateCanvasState(e) {
- if (!this.canUndoState) {
- const canvasAsJson = JSON.stringify(this.fcanvas.toJSON(['name','sort','mtr','id','selectable','erasable']));
- this.canvasState.splice(this.stateIndex+1);
- this.canvasState.push(canvasAsJson);
- this.stateIndex = this.canvasState.length - 1;
- // if(this.viewStatus) this.updateMiniMap();
- } else {
- this.canUndoState = false;
- }
- },
- }
- }
|