(function() { fabric.enableGLFiltering = false; fabric.isWebglSupported = false; fabric.Object.prototype.objectCaching = true; var visualTestLoop; if (fabric.isLikelyNode) { visualTestLoop = global.visualTestLoop; } else { visualTestLoop = window.visualTestLoop; } var fabricCanvas = this.canvas = new fabric.Canvas(null, { enableRetinaScaling: false, renderOnAddRemove: false, width: 200, height: 200, }); function svgToDataURL(svgStr) { var encoded = encodeURIComponent(svgStr) .replace(/'/g, '%27') .replace(/"/g, '%22'); return 'data:image/svg+xml,' + encoded; } function toSVGCanvas(canvas, callback) { var svg = canvas.toSVG(); var dataUrl = svgToDataURL(svg); var image = fabric.document.createElement('img'); image.onload = function() { var newCanvas = fabric.util.createCanvasElement(); newCanvas.width = canvas.width; newCanvas.height = canvas.height; newCanvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height); callback(newCanvas); }; image.onerror = console.log; if (fabric.isLikelyNode) { image.src = dataUrl; } else { image.src = dataUrl; } } var tests = []; function clipping0(canvas, callback) { var clipPath = new fabric.Circle({ radius: 100, strokeWidth: 0, top: -10, left: -10 }); var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 200, height: 200, fill: 'rgba(0,255,0,0.5)'}); obj.clipPath = clipPath; canvas.add(obj); toSVGCanvas(canvas, callback); } tests.push({ test: 'Clip a rect with a circle, no zoom', code: clipping0, golden: 'clipping0.png', newModule: 'Export clippaths to SVG', percentage: 0.06, }); function clipping01(canvas, callback) { var clipPath = new fabric.Circle({ radius: 50, strokeWidth: 40, top: -50, left: -50, fill: 'transparent' }); var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 200, height: 200, fill: 'rgba(0,255,0,0.5)'}); obj.clipPath = clipPath; canvas.add(obj); toSVGCanvas(canvas, callback); } tests.push({ test: 'A clippath ignores fill and stroke for drawing, not positioning', code: clipping01, golden: 'clipping01.png', percentage: 0.06, }); function clipping1(canvas, callback) { var zoom = 20; canvas.setZoom(zoom); var clipPath = new fabric.Circle({ radius: 5, strokeWidth: 0, top: -2, left: -2 }); var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 10, height: 10, fill: 'rgba(255,0,0,0.5)'}); obj.clipPath = clipPath; canvas.add(obj); toSVGCanvas(canvas, callback); } tests.push({ test: 'Clip a rect with a circle, with zoom', code: clipping1, golden: 'clipping1.png', percentage: 0.06, }); function clipping2(canvas, callback) { var clipPath = new fabric.Circle({ radius: 100, top: -100, left: -100 }); var group = new fabric.Group([ new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'red' }), new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'yellow', left: 100 }), new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'blue', top: 100 }), new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'green', left: 100, top: 100 }) ], { strokeWidth: 0 }); group.clipPath = clipPath; canvas.add(group); toSVGCanvas(canvas, callback); } tests.push({ test: 'Clip a group with a circle', code: clipping2, golden: 'clipping2.png', percentage: 0.06, }); function clipping3(canvas, callback) { var clipPath = new fabric.Circle({ radius: 100, top: -100, left: -100 }); var small = new fabric.Circle({ radius: 50, top: -50, left: -50 }); var small2 = new fabric.Rect({ width: 30, height: 30, top: -50, left: -50 }); var group = new fabric.Group([ new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'red', clipPath: small }), new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'yellow', left: 100 }), new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'blue', top: 100, clipPath: small2 }), new fabric.Rect({ strokeWidth: 0, width: 100, height: 100, fill: 'green', left: 100, top: 100 }) ], { strokeWidth: 0 }); group.clipPath = clipPath; canvas.add(group); toSVGCanvas(canvas, callback); } tests.push({ test: 'Isolation of clipPath of group and inner objects', code: clipping3, golden: 'clipping3.png', percentage: 0.06, disabled: false, }); function clipping4(canvas, callback) { var clipPath = new fabric.Circle({ radius: 20, strokeWidth: 0, top: -10, left: -10, scaleX: 2, skewY: 45 }); var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 200, height: 200, fill: 'rgba(0,255,0,0.5)'}); obj.fill = new fabric.Gradient({ type: 'linear', coords: { x1: 0, y1: 0, x2: 200, y2: 200, }, colorStops: [ { offset: 0, color: 'red', }, { offset: 1, color: 'blue', } ] }); obj.clipPath = clipPath; canvas.add(obj); toSVGCanvas(canvas, callback); } tests.push({ test: 'ClipPath can be transformed', code: clipping4, golden: 'clipping4.png', percentage: 0.06, }); function clipping5(canvas, callback) { var clipPath = new fabric.Circle({ radius: 20, strokeWidth: 0, top: -10, left: -10, scaleX: 2, skewY: 45 }); var clipPath1 = new fabric.Circle({ radius: 15, rotate: 45, strokeWidth: 0, top: -100, left: -50, scaleX: 2, skewY: 45 }); var clipPath2 = new fabric.Circle({ radius: 10, strokeWidth: 0, top: -20, left: -20, scaleY: 2, skewX: 45 }); var group = new fabric.Group([clipPath, clipPath1, clipPath2]); var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 200, height: 200, fill: 'rgba(0,255,0,0.5)'}); obj.fill = new fabric.Gradient({ type: 'linear', coords: { x1: 0, y1: 0, x2: 200, y2: 200, }, colorStops: [ { offset: 0, color: 'red', }, { offset: 1, color: 'blue', } ] }); obj.clipPath = group; canvas.add(obj); toSVGCanvas(canvas, callback); } tests.push({ test: 'ClipPath can be a group with many objects', code: clipping5, golden: 'clipping5.png', percentage: 0.06, disabled: false, }); function clipping6(canvas, callback) { var clipPath = new fabric.Circle({ radius: 20, strokeWidth: 0, top: -10, left: -10, scaleX: 2, skewY: 45 }); var clipPath1 = new fabric.Circle({ radius: 15, rotate: 45, strokeWidth: 0, top: -100, left: -50, scaleX: 2, skewY: 45 }); var clipPath2 = new fabric.Circle({ radius: 10, strokeWidth: 0, top: -20, left: -20, scaleY: 2, skewX: 45 }); var group = new fabric.Group([clipPath, clipPath1, clipPath2]); var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 200, height: 200, fill: 'rgba(0,255,0,0.5)'}); obj.fill = new fabric.Gradient({ type: 'linear', coords: { x1: 0, y1: 0, x2: 200, y2: 200, }, colorStops: [ { offset: 0, color: 'red', }, { offset: 1, color: 'blue', } ] }); obj.clipPath = group; group.inverted = true; canvas.add(obj); toSVGCanvas(canvas, callback); } tests.push({ test: 'ClipPath can be inverted, it will clip what is outside the clipPath', code: clipping6, golden: 'clipping6.png', percentage: 0.06, disabled: true, }); function clipping7(canvas, callback) { var clipPath = new fabric.Circle({ radius: 30, strokeWidth: 0, top: -30, left: -30, skewY: 45 }); var obj1 = new fabric.Rect({ top: 0, left: 100, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(0,255,0,0.8)'}); var obj2 = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(255,255,0,0.8)'}); var obj3 = new fabric.Rect({ top: 100, left: 0, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(0,255,255,0.8)'}); var obj4 = new fabric.Rect({ top: 100, left: 100, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(255,0,0,0.8)'}); obj1.clipPath = clipPath; obj2.clipPath = clipPath; obj3.clipPath = clipPath; obj4.clipPath = clipPath; canvas.add(obj1); canvas.add(obj2); canvas.add(obj3); canvas.add(obj4); toSVGCanvas(canvas, callback); } tests.push({ test: 'Many Objects can share the same clipPath', code: clipping7, golden: 'clipping7.png', percentage: 0.06, disabled: false, }); function clipping8(canvas, callback) { var clipPath = new fabric.Circle({ radius: 60, strokeWidth: 0, top: 40, left: 40, absolutePositioned: true }); var obj1 = new fabric.Rect({ top: 0, left: 100, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(0,255,0,0.8)'}); var obj2 = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(255,255,0,0.8)'}); var obj3 = new fabric.Rect({ top: 100, left: 0, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(0,255,255,0.8)'}); var obj4 = new fabric.Rect({ top: 100, left: 100, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(255,0,0,0.8)'}); obj1.clipPath = clipPath; obj2.clipPath = clipPath; obj3.clipPath = clipPath; canvas.add(obj1); canvas.add(obj2); canvas.add(obj3); canvas.add(obj4); toSVGCanvas(canvas, callback); } tests.push({ test: 'an absolute positioned clipPath, shared', code: clipping8, golden: 'clipping8.png', percentage: 0.06, disabled: false, }); function clipping9(canvas, callback) { var clipPath = new fabric.Circle({ radius: 60, strokeWidth: 0, top: 10, left: 10 }); var obj1 = new fabric.Rect({ top: 0, left: 100, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(0,255,0,0.8)'}); var obj2 = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(255,255,0,0.8)'}); var obj3 = new fabric.Rect({ top: 100, left: 0, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(0,255,255,0.8)'}); var obj4 = new fabric.Rect({ top: 100, left: 100, strokeWidth: 0, width: 100, height: 100, fill: 'rgba(255,0,0,0.8)'}); canvas.add(obj1); canvas.add(obj2); canvas.add(obj3); canvas.add(obj4); canvas.clipPath = clipPath; toSVGCanvas(canvas, callback); } tests.push({ test: 'a clipPath on the canvas', code: clipping9, golden: 'clipping9.png', percentage: 0.06, disabled: false, }); tests.forEach(visualTestLoop(fabricCanvas, QUnit)); })();