額。。這是我本身創造的名詞,內嵌工做臺畫布javascript
如圖(http://fabricjs.com/kitchensink): java
首先想元素脫出畫布外一部分也能選中,如圖上面就無法選擇超出框外的內容。我想到的是如何在畫布中間加個畫布。周圍隱藏掉。json
其餘思路
- 使用clipPath,畫一個通道給中間區域。
- 畫兩個畫布一個再上面一個在下面 其實這些方法可能都能實現,可是問題的關鍵不是實現,而是如何兼容以後的諸多行爲,好比zoom,loadJson,redo, undo
1.先定義一個想要的外邊距padding,而後計算內部工做臺處於外部畫布的四個點的位置。canvas
// 獲取中間畫布的四個點的位置 function setXY(state, obj = {}) { // 最外邊整個畫布的寬 高 const w = obj.currentWidth || state.currentWidth const h = obj.currentHeight || state.currentHeight // 內部工做臺畫布的寬pannelW 高pannelH if ( state.currentWidth / state.currentHeight > state.pannelW / state.pannelH ) { const height = h - state.padding // 縮放畫布,由於pannelW爲實際寬度,好比一張A4紙顯示在屏幕上,須要縮放一下,zoom後獲取到屏幕的寬度 state.zoom = height / state.pannelH this.commit('zoomCanvas') const x2 = 0 const y2 = 0 const y0 = state.padding / state.zoom / 2 + y2 const y1 = state.pannelH + y0 const x0 = w / state.zoom / 2 - state.pannelW / 2 + x2 const x1 = state.pannelW + x0 state.arrX = [x0, x1, x2, w / state.zoom + x2] state.arrY = [y0, y1, y2, h / state.zoom + y2] } else { const width = w - state.padding state.zoom = width / state.pannelW this.commit('zoomCanvas') const x2 = 0 const y2 = 0 const x0 = state.padding / state.zoom / 2 + x2 const x1 = state.pannelW + x0 const y0 = h / state.zoom / 2 - state.pannelH / 2 + y2 const y1 = state.pannelH + y0 state.arrX = [x0, x1, x2, w / state.zoom] state.arrY = [y0, y1, y2, h / state.zoom] } }
2.建立四周的遮罩,給中間的區域,畫個背景測試
function createMask(state) { const arrX = state.arrX const arrY = state.arrY const pathOption = { selectable: false, fill: '#ebeced', hoverCursor: 'default', evented: false, excludeFromExport: true, hasControls: false, perPixelTargetFind: false, strokeWidth: 0, stroke: null } const rect1 = new fabric.Rect({ width: arrX[0] - arrX[2], height: arrY[3] - arrY[2] }) const rect2 = new fabric.Rect({ width: arrX[3] - arrX[0] + 2, height: arrY[0] - arrY[2] }) const rect3 = new fabric.Rect({ width: arrX[3] - arrX[1], height: arrY[1] - arrY[0] + 2 }) const rect4 = new fabric.Rect({ width: arrX[3] - arrX[0], height: arrY[3] - arrY[1] }) rect1.set({ left: arrX[2], top: arrY[2], name: 'mask1', ...pathOption }) rect2.set({ left: arrX[0] - 1, top: arrY[2], name: 'mask2', ...pathOption }) rect3.set({ left: arrX[1], top: arrY[0] - 1, name: 'mask3', ...pathOption }) rect4.set({ left: arrX[0], top: arrY[1], name: 'mask4', ...pathOption }) state.maskPath = new fabric.Group([rect1, rect2, rect3, rect4], { selectable: false, excludeFromExport: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, lockUniScaling: true, hoverCursor: 'auto', name: 'grid', left: arrX[2], top: arrY[2], type: 'sMask', evented: false }) // 建立中間畫布的背景 state.backgroundPanel = new fabric.Rect({ left: arrX[0], top: arrY[0], selectable: false, evented: false, width: state.pannelW, height: state.pannelH, strokeWidth: 0, fill: !state.activePage ? '#ffffff' : 'rgba(0, 0, 0, 0)', objectCaching: false, hoverCursor: 'default', excludeFromExport: true, hasControls: false, type: 'sBg', perPixelTargetFind: false }) state.canvas.remove(...state.canvas.getObjects('sMask')) state.canvas.remove(...state.canvas.getObjects('sBg')) state.canvas.add(state.maskPath) state.canvas.add(state.backgroundPanel) state.canvas.sendToBack(state.backgroundPanel) state.canvas.renderAll() }
3.刷新畫布,或者加載數據的時候this
jsonDemo.objects.forEach((item) => { item.left += state.arrX[0] item.top += state.arrY[0] })
4.保存jsonspa
function saveJson(state) { // state.toJSONProperties 爲要保存的額外屬性 const jsonStr = JSON.stringify(state.canvas.toJSON(state.toJSONProperties)) const jsonObj = JSON.parse(jsonStr) jsonObj.objects.forEach((v) => { v.left = v.left - state.arrX[0] v.top = v.top - state.arrY[0] }) }