canvas插件 fabric.js 使用

2023-05-16

fabric.js使用

  • fabric.js 是 常用的 canvas 插件
    • 1, 在项目中使用
    • 2, 特殊用法
      • ①, 基本设置
      • ②, 画板数据的导入导出
      • ③, 遮罩 Pattern ( 引用官网案例 )
      • ④, 多个对象合并, 并设置为 fabric 背景 ( 适用于变色和更多场景 )
      • ⑤, 把 canvas对象 或者 fabric对象 导出为图片
      • ⑥, 位置的获取
        • 思路一: (计算法, 没算出来)
        • 思路二 (记录位置) 补充: 鼠标位移留痕不规则线条在如下代码中
    • 注意事项

fabric.js 是 常用的 canvas 插件

官方地址:  http://fabricjs.com/
小编用的版本为 4.5.1

1, 在项目中使用

		#myCanvas{
          width:10000px;
          height:10000px;
        }
<canvas id="myCanvas" width="10000" height="10000"></canvas>
<!-- 此处引用jquery ,也可以不引用 -->
<script src="./jquery-2.0.0.min.js"></script>
<script src="./fabric.5.1.0.min.js"></script>
		// 初始化 canvas 为 fabric 对象
        var canvas = this.__canvas = new fabric.Canvas('myCanvas');

		// 画一个矩形
        var rect = new fabric.Rect({
            width: 1000,
            height: 1000,
            left: 0,
            top: 0,
            fill: 'rgba(255, 0, 0, 0.4)',
        });
		// canvas 添加矩形并渲染
        canvas.add( rect );
        canvas.renderAll();

2, 特殊用法

    下面是在使用时用到和遇到的场景

①, 基本设置

		//画板元素不能被选中
        canvas.skipTargetFind = false;
        //画板不显示选中
        canvas.selection = false;
        canvas.freeDrawingBrush.color = "red";
        canvas.freeDrawingBrush.width = 1;
        //画板不限制调整时的宽高比
        // mycanvas.uniScaleTransform = true;
        // //绑定画板事件
        canvas.on({
            // 'mouse:dblclick': this.dblShowRectFrom,//双击区域弹出
            'mouse:down': this.mouseDownDrawRect, //按下,
            'mouse:move': this.mouseMoving, //移动中,
            'mouse:up': this.mouseUpDrawRect, //抬起,
            'mouse:wheel': this.zoomsHandle, //缩放
            // 'object:moved': this.objectMoved, //移动完成
            // // 'object:moving': objectMov,//移动中
            // 'object:scaled': this.mouseScaleDrawRect,//缩放完成
            // 'object:scaling': this.mouseScalingDrawRect,//缩放中
        });

②, 画板数据的导入导出

	// 1, 导出  检测 画板上所有对象 并保存为 字符串
	 let inkTrace = [];
	 canvas.getObjects().forEach((v,i) => {
            let klass = v.toJSON();
            if(klass.type == 'image'){
            	// 对其中一些特殊对象进行处理
            }
            inkTrace.push(klass)
      })
      // 对数据最后加工
      if(inkTrace.length==0){inkTrace=""}else{inkTrace = JSON.stringify(inkTrace)}
	// 2, 导入 根据字符串处理为 fabric 可识别的对象
			JSON.parse(inkTrace).forEach((v,i) => {
				// 不同类型的数据要分别处理 数据中的 type 属性
                if(v.type=="image"){
                    new fabric.Image.fromObject(v, (e) => {
                        mycanvas.add(e)
                    })
                }
                if(v.type=="path"){
                    new fabric.Path.fromObject(v, (e) => {
                        canvas.add(e)
                    })
                }
            })
            canvas.renderAll();

③, 遮罩 Pattern ( 引用官网案例 )

		var text = new fabric.Text('Honey,\nI\'m subtle', {
            fontSize: 250,
            left: 0,
            top: 0,
            lineHeight: 1,
            originX: 'left',
            fontFamily: 'Helvetica',
            fontWeight: 'bold',
            statefullCache: true,
            scaleX: 0.4,
            scaleY: 0.4
        });
        var shape = new fabric.Rect({
            width: 200,
            height: 100,
            left: 10,
            top: 300,
        });
        canvas.add(text, shape);
        loadPattern('./xxx.png');
        function loadPattern(url) {
            fabric.util.loadImage(url, function(img) {
            console.log(img)
            text.set('fill', new fabric.Pattern({
                source: img,
                repeat: "no-repeat"
            }));
            rect.set('fill', new fabric.Pattern({
                source: img,
                repeat: "no-repeat"
            }));
            canvas.renderAll();
            });
        }

④, 多个对象合并, 并设置为 fabric 背景 ( 适用于变色和更多场景 )

			// 创建对象
			let image = new fabric.Image(image, {
                left: 10,
                top: 10,
                scaleX: 1,
                scaleY: 1,
                crossOrigin: 'anonymous',// 跨域
            })
            let shape = new fabric.Rect({
                width: image.width,
                height: image.height,
                opacity:0.4,
                fill: "#FF0000",
                left: 10,
                top: 10,
                scaleX: 1,
                scaleY: 1,
            });
            // 合并
            let group = new fabric.Group([image, shape], {})
            // 设置为背景
            canvas.setBackgroundImage(group, canvas.renderAll.bind(canvas), {});

⑤, 把 canvas对象 或者 fabric对象 导出为图片

		let image = new Image();
		// 先引入一张图片作为底图, 不引入也没关系
        image.src = './xxx.png';
        image.setAttribute('crossOrigin', 'anonymous');
        image.onload = () => {
            let fabricImage = new fabric.Image(image, {
                left: 0,
                top: 0,
                width: image.width,
                height: image.height
            })
            let shapeLine = new fabric.Rect({
                width: 1700,
                height: 800,
                fill: "#333",
                left: 0,
                top: 0,
            });
            let shapeVert = new fabric.Rect({
                width: 235,
                height: image.height,
                fill: "#333",
                left: 0,
                top: 0,
            });
            // 把这几个对象合并一下
            let group = new fabric.Group([fabricImage, shapeLine, shapeVert], {})
            let imgs = group.toDataURL({
                format: 'png', // jpeg或png
                left: 0,
                top: 0,
                width: image.width,
                height: image.height
            });
            // imgs 就是导出后的图片, 放在 img 的src中即可使用
        }

⑥, 位置的获取

应用场景: 要求背景为一块矩形, 所有的遮罩, 痕迹, 只准出现在背景矩形上, 不准超出

思路一: (计算法, 没算出来)

在有旋转角度的情况下,
     位移: 算出位移之后出现在矩形上的位置(如,右侧超出出现在右侧)
     缩放: 在有旋转角度的情况下, 拉伸已旋转的矩形, 如果超出背景矩形, 则缩放至最大高度(没算出来)
     旋转: 旋转矩形如果超出背景矩形, 计算矩形能在背景矩形旋转的最大角度(没算出来)

mycanvas.on({
            //'mouse:down': this.mouseDownDrawRect, //按下,
            //'mouse:move': this.mouseMoving, //移动中,
            //'mouse:up': this.mouseUpDrawRect, //抬起,
            //'mouse:wheel': this.zoomsHandle, //缩放
            'object:moved': this.objectMov, //移动完成
            //'object:moving': this.objectMov,//移动中
            'object:scaled': this.mouseScaleDrawRect,//缩放完成
            //'object:scaling': this.mouseScalingDrawRect,//缩放中
            'object:rotated': this.objectRotated,// 旋转完成
            //'object:rotating': this.objectRotated,// 旋转中
        });
        // 移动完成计算
        objectMov = (t) => {
        const { mainCanvas } = this.state;
        let mycanvas = mainCanvas,oImgList = mycanvas.getObjects(),oImg = t?oImgList[oImgList.length-1]:mycanvas.getActiveObject();
        if(!oImg) { return false;}
        let ep = oImg.aCoords,// 目标对象的四个坐标点
            angle= Math.floor(( oImg.angle ) / 90),// 目标对象的旋转角度
            ptl = mycanvas.backgroundImage.aCoords.tl,
            pbr = mycanvas.backgroundImage.aCoords.br,et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0;
        switch (angle) {
            case 0:
                et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
                el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.bl.x }); // 左侧超出
                if(er > pbr.x) { // 右侧超出
                    if((ep.tr.x - ep.bl.x) <= (pbr.x - ptl.x)) { oImg.set({ left:pbr.x - (ep.tr.x - ep.tl.x) })}
                    if((ep.tr.x - ep.bl.x) > (pbr.x - ptl.x)) { oImg.set({ left:ptl.x }) }
                }
                et < ptl.y && oImg.set({ top:ptl.y })// 上侧超出
                if(eb > pbr.y) { // 下侧超出
                    if((ep.br.y - ep.tl.y) <= (pbr.y - ptl.y)) { oImg.set({ top:pbr.y - (ep.br.y - ep.tl.y) })}
                    if((ep.br.y - ep.tl.y) > (pbr.y - ptl.y)) { oImg.set({ top:ptl.y }) }
                }
                break;
            case 1:
                et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
                er > pbr.x && oImg.set({ left:pbr.x  })// 右侧超出
                el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.br.x });// 左侧超出
                et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.bl.y) })// 上侧超出
                eb > pbr.y && oImg.set({ top:pbr.y - (ep.tr.y - ep.tl.y) })// 下侧超出
                break;
            case 2:
                et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
                el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.tr.x });// 左侧超出
                er > pbr.x && oImg.set({ left:pbr.x - (ep.bl.x - ep.tl.x)})// 右侧超出
                et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.br.y) })// 上侧超出
                eb > pbr.y && oImg.set({ top:pbr.y  })// 下侧超出
                break;
            case 3:
                et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
                el < ptl.x && oImg.set({ left:ptl.x }); // 左侧超出
                er > pbr.x && oImg.set({ left:pbr.x - (ep.br.x - ep.tl.x)})// 右侧超出
                et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.tr.y) })// 上侧超出
                eb > pbr.y && oImg.set({ top:pbr.y - (ep.bl.y - ep.tl.y) })// 下侧超出
        }
        mycanvas.renderAll()
    }
获取所有的对象: mycanvas.getObjects()
获取最后一个对象: var objList = mycanvas.getObjects(),obj = objList[objList.length-1]
获取被选中的对象: mycanvas.getActiveObject()

缩放和旋转没算出来, 代码删了留着日后解决,

思路二 (记录位置) 补充: 鼠标位移留痕不规则线条在如下代码中

方法非常简单, mousedown记录选中矩形状态信息, mouseup检测是否超出, 是则回退至原来的位置

// objectMov 位移
mycanvas.on({
            'mouse:down': this.mouseDownDrawRect, //按下,
            'mouse:move': this.mouseMoving, //移动中,
            'mouse:up': this.mouseUpDrawRect, //抬起,
            //'mouse:wheel': this.zoomsHandle, //缩放
            //'object:moved': this.objectMov, //移动完成
            //'object:moving': this.objectMov,//移动中
            //'object:scaled': this.mouseScaleDrawRect,//缩放完成
            //'object:scaling': this.mouseScalingDrawRect,//缩放中
            //'object:rotated': this.objectRotated,// 旋转完成
            //'object:rotating': this.objectRotated,// 旋转中
        });
        mouseDownDrawRect = () => {
            _this_obj : JSON.parse(JSON.stringify(mycanvas.getActiveObject()))
    	};
    	mouseMoving = (options) => {
    	// canvasPointSave 在 mouseUpDrawRect 中定义
        // 判断是否可以留痕
        if( chousedType == 0 && mycanvas.backgroundImage && canvasPointSave.tl){
            let ptl = canvasPointSave.tl,
            pbr = canvasPointSave.br,
            eposition = options.pointer;
            if ( eposition.x < ptl.x || eposition.x > pbr.x || eposition.y < ptl.y || eposition.y > pbr.y) {
                mycanvas.isDrawingMode = false;
            }else{
                mycanvas.isDrawingMode = true;
            }
        }
    };
    	mouseUpDrawRect = (options) => {
    	// 如果是鼠标画不规则线条, 则重新判断位置
    	mycanvas.isDrawingMode = false;
        let canvasObjArr = mycanvas.getObjects();
        if( chousedType == 0 && canvasObjArr.length > 0){
            this.referDrawRect();
        }
        // 如果是位移,缩放,旋转则计算位置
        if( options.transform ){
            if( options.transform.action== "drag"){
                this.objectMov();
            }
            if( options.transform.action== "scaleX" ||  options.transform.action== "scaleY" ||  options.transform.action== "scale"){
                this.mouseScalingDrawRect();this.objectMov();
            }
            if( options.transform.action== "rotate"){
                this.objectRotated();this.objectMov();
            }
        }else if(options.target){
            this.objectMov();
        }
        // 记录位置
        if(mycanvas.backgroundImage) { canvasPointSave = mycanvas.backgroundImage.lineCoords }
    };
    objectMov = (t) => {
        const { mainCanvas } = this.state;
        let mycanvas = mainCanvas,oImgList = mycanvas.getObjects(),oImg = t?oImgList[oImgList.length-1]:mycanvas.getActiveObject();
        if(!oImg) { return false;}
        let ep = oImg.aCoords,// 目标对象的四个坐标点
            angle= Math.floor(( oImg.angle ) / 90),// 目标对象的旋转角度
            ptl = mycanvas.backgroundImage.aCoords.tl,
            pbr = mycanvas.backgroundImage.aCoords.br,et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0;
        switch (angle) {
            case 0:
                et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
                el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.bl.x }); // 左侧超出
                if(er > pbr.x) { // 右侧超出
                    if((ep.tr.x - ep.bl.x) <= (pbr.x - ptl.x)) { oImg.set({ left:pbr.x - (ep.tr.x - ep.tl.x) })}
                    if((ep.tr.x - ep.bl.x) > (pbr.x - ptl.x)) { oImg.set({ left:ptl.x }) }
                }
                et < ptl.y && oImg.set({ top:ptl.y })// 上侧超出
                if(eb > pbr.y) { // 下侧超出
                    if((ep.br.y - ep.tl.y) <= (pbr.y - ptl.y)) { oImg.set({ top:pbr.y - (ep.br.y - ep.tl.y) })}
                    if((ep.br.y - ep.tl.y) > (pbr.y - ptl.y)) { oImg.set({ top:ptl.y }) }
                }
                break;
            case 1:
                et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
                er > pbr.x && oImg.set({ left:pbr.x  })// 右侧超出
                el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.br.x });// 左侧超出
                et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.bl.y) })// 上侧超出
                eb > pbr.y && oImg.set({ top:pbr.y - (ep.tr.y - ep.tl.y) })// 下侧超出
                break;
            case 2:
                et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
                el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.tr.x });// 左侧超出
                er > pbr.x && oImg.set({ left:pbr.x - (ep.bl.x - ep.tl.x)})// 右侧超出
                et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.br.y) })// 上侧超出
                eb > pbr.y && oImg.set({ top:pbr.y  })// 下侧超出
                break;
            case 3:
                et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
                el < ptl.x && oImg.set({ left:ptl.x }); // 左侧超出
                er > pbr.x && oImg.set({ left:pbr.x - (ep.br.x - ep.tl.x)})// 右侧超出
                et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.tr.y) })// 上侧超出
                eb > pbr.y && oImg.set({ top:pbr.y - (ep.bl.y - ep.tl.y) })// 下侧超出
        }
        mycanvas.renderAll()
    }
    mouseScalingDrawRect = () => {
        const { mainCanvas,_this_obj } = this.state;
        let mycanvas = mainCanvas,obj = mycanvas.getActiveObject();
        if(!obj) { return false;}
        let ep = obj.aCoords,// 目标对象的四个坐标点
            angle= Math.floor(( obj.angle ) / 90),et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0;// 目标对象的旋转角度
        switch (angle) {
            case 0:
                et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
                w = ep.tr.x - ep.bl.x ;h = ep.br.y - ep.tl.y ;
                break;
            case 1:
                et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
                w = ep.tl.x - ep.br.x ;h = ep.tr.y - ep.bl.y ;
                break;
            case 2:
                et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
                w = ep.bl.x - ep.tr.x ;h = ep.tl.y - ep.br.y ;
                break;
            case 3:
                et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
                w = ep.br.x - ep.tl.x ;h = ep.bl.y - ep.tr.y ;
        }
        if(w > mycanvas.backgroundImage.width || h > mycanvas.backgroundImage.height){
            obj.set({ 
                scaleY: _this_obj.scaleY,
                scaleX: _this_obj.scaleX,
                left: _this_obj.left,
                top: _this_obj.top,
            }) 
        }
        mycanvas.renderAll()
    }
    referDrawRect = () => {
        const { mainCanvas } = this.state;
        let mycanvas = mainCanvas,objList = mycanvas.getObjects(),obj = objList[objList.length-1];
        if(!obj) { return false;}
        let scx = mycanvas.backgroundImage.width / obj.width;// -0.05
        let scy = mycanvas.backgroundImage.height / obj.height;// -0.05
        if (obj.scaleX > scx) {
            obj.set({scaleX:scx})
        };
        if (obj.scaleY > scy) {
            obj.set({scaleY:scy})
        };
        mycanvas.renderAll()
        this.objectMov(true)
    }
    objectRotated = () => {
        const { mainCanvas, _this_obj } = this.state;
        let mycanvas = mainCanvas,oImg = mycanvas.getActiveObject();// 目标对象
        if(!oImg) { return false;}
        let ep = oImg.aCoords,// 目标对象的四个坐标点
            angle= Math.floor(( oImg.angle ) / 90),// 目标对象的旋转角度
            ptl = mycanvas.backgroundImage.aCoords.tl,
            pbr = mycanvas.backgroundImage.aCoords.br,et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0,lock = false;
        switch (angle) {
            case 0:
                et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
                w = ep.tr.x - ep.bl.x ;h = ep.br.y - ep.tl.y ;
                break;
            case 1:
                et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
                w = ep.tl.x - ep.br.x ;h = ep.tr.y - ep.bl.y ;
                break;
            case 2:
                et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
                w = ep.bl.x - ep.tr.x ;h = ep.tl.y - ep.br.y ;
                break;
            case 3:
                et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
                w = ep.br.x - ep.tl.x ;h = ep.bl.y - ep.tr.y ;
        }
        if(w > mycanvas.backgroundImage.width || h > mycanvas.backgroundImage.height ) {
            oImg.set({ 
                angle: _this_obj.angle,
                left: _this_obj.left,
                top: _this_obj.top,
            }) 
        }
        mycanvas.renderAll()
    }

注意事项

1, canvas 使用和 img 相关操作时 如果 跨域 需要加上 crossOrigin: 'anonymous' 属性;
2, fabric.js 是根据 html 中 canvas标签的id 来初始化的, 如需 初始化多个fabric , 则需 注意 canvas 标签的 id, 以及 window.全局配置的参数(使用vue和react时)
3, fabric 初始化过后的画布, 如果发生 位移缩放 , 再进行放置对象是要 进行计算


计算规则如下
// 发生位移计算
			// 右上角
            let tr = canvas.backgroundImage.lineCoords.tr;
            // 右下角
            let br = canvas.backgroundImage.lineCoords.br;
            // 左上角
            let tl = canvas.backgroundImage.lineCoords.tl;
            // 左下角
            let bl = canvas.backgroundImage.lineCoords.bl;
            //判断试卷最右侧 是否离开了可视区域
            if (tr.x <= 50) {
                var delta = new fabric.Point(50, 0);
                relativeMouseX += 50;
                canvas.relativePan(delta);
                return;
            }
            //判断试卷最上侧 是否离开了可视区域
            if (tr.y > screenHeight - 50) {
                var delta = new fabric.Point(0, -50);
                relativeMouseY -= 50;
                canvas.relativePan(delta);
                return;
            }
            //判断试卷最下侧 是否离开了可视区域
            if (br.y <= 50) {
                var delta = new fabric.Point(0, 50);
                relativeMouseY += 50;
                canvas.relativePan(delta);
                return;
            }
            //判断试卷最左侧 是否离开了可视区域
            if (tl.x > screenWidth - 50) {
                var delta = new fabric.Point(-50, 0);
                relativeMouseX -= 50;
                canvas.relativePan(delta);
                return;
            }

            var delta = new fabric.Point(options.e.movementX, options.e.movementY);
            canvas.relativePan(delta);
            relativeMouseX += options.e.movementX; //累计每一次移动时候的偏差
            relativeMouseY += options.e.movementY;





// 发生缩放计算
	//  缩放代码
		let wheelPost = e?e.e.deltaY:wheel,
            pointerX = e.pointer.x,
            pointerY = e.pointer.y,
            zoomSpeed = 0.03,
            zoom, zoomPoint, lastzoom, lastzoomPoint={x:0,y:0}, lastmousePoint={x:0,y:0}, relativeMouseX, relativeMouseY;// 通过zoomSpeed 控制缩放速度
        zoom = (wheelPost > 0 ? -zoomSpeed : zoomSpeed) + canvas.getZoom();
        zoom = Math.max(0.1, zoom); //最小为原来的1/10
        zoom = Math.min(3, zoom); //最大是原来的3倍
        zoomPoint = new fabric.Point(pointerX, pointerY);
        canvas.zoomToPoint(zoomPoint, zoom);

        lastzoomPoint.x =
            lastzoomPoint.x + (zoomPoint.x - lastmousePoint.x - relativeMouseX) / lastzoom;
        lastzoomPoint.y =
            lastzoomPoint.y + (zoomPoint.y - lastmousePoint.y - relativeMouseY) / lastzoom;

        lastmousePoint.x = zoomPoint.x;
        lastmousePoint.y = zoomPoint.y;
        lastzoom = zoom;

        relativeMouseX = 0;
        relativeMouseY = 0;

// 需要获取值时
	transformMouse = (mouseX, mouseY) => {
        let { lastzoomPoint,zoomPoint,relativeMouseX,relativeMouseY } = this.state;
        let {
            mainCanvas
        } = this.state;

        let mycanvas = mainCanvas;

        let x = lastzoomPoint.x + (mouseX - zoomPoint.x - relativeMouseX) / mycanvas.getZoom();
        let y = lastzoomPoint.y + (mouseY - zoomPoint.y - relativeMouseY) / mycanvas.getZoom();
        return { x, y };
    };
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

canvas插件 fabric.js 使用 的相关文章

  • 如何制作带有圆角的 tkinter 画布矩形?

    我想创建一个带圆角的矩形 我正在使用 tkinter 的画布 为托比亚斯的方法提供另一种方法确实是用一个多边形来实现 如果您担心优化 或者不必担心引用单个对象的标签系统 那么这将具有成为一个画布对象的优点 该代码有点长 但非常基本 因为它只
  • 在 Chrome 中拖动时更改 HTML5 Canvas 上的光标

    我正在研究如何在拖动鼠标时更改 HTML5 画布上的光标 遇到了这个 拖动鼠标时更改 HTML5 Canvas 上的光标 https stackoverflow com questions 4945874 change cursor ove
  • delphi 文本矩形 自动换行

    我在 Delphi 7 中使用 Canvas TextRect 在屏幕上输出一些内容 我需要将文本包裹在矩形中 您需要使用带有 DT WORDBREAK 标志的 DrawText 或 DrawTextEx 请参阅中的示例
  • 如何为浏览器生成CMY三角形? (CSS、SVG、画布?)

    我正在尝试为一个项目生成一个 CMY 三角形 我希望能够使用 css svg 或 canvas 直接在网络上渲染它 根据 Amelia 的评论 我尝试构建一个 CSS SASS 类来用线性渐变渲染三角形 triangle include b
  • 测量缩放画布上的文本

    我一直在努力处理文本测量和缩放画布 当画布未缩放时 getTextBounds 和measureText 可提供准确的结果 但是 当缩放画布时 这两种方法都无法提供与打印文本的实际大小相匹配的结果 为了进行测试 我使用以下 onDraw 方
  • 在画布元素中使用 SVG Evenodd 填充规则

    有谁知道是否可以通过 JavaScript 原生或数学方式实现evenodd fill rule来自 HTML5 canvas 元素中的 SVG 执行此操作的项目的链接会很有帮助 是的 应该可以globalCompositeOperatio
  • Javascript画布不重画?

    我正在开发一个游戏 基于画布 并且遇到了问题 显然 当我按下一个键时 画布并没有更新对象的 x 和 y 它什么也没做 变量本身正在更新 但屏幕上的对象没有更新 这是代码 var canvas document createElement c
  • Firefox:drawImage(视频)失败并显示 NS_ERROR_NOT_AVAILABLE:组件不可用

    尝试打电话drawImage with a video其来源是网络摄像头源似乎在 Firefox 中失败了NS ERROR NOT AVAILABLE Component is not available 我尝试等待视频标签触发的每个事件
  • 有类似 Fabric for Perl 的东西吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我很喜欢Fabric http fabfile org 对于个人项目 但我的日常工作是在面向 Perl
  • Fabric.js canvas.toDataURL() 通过 Ajax 发送到 PHP

    当我需要创建具有透明背景的图像时 我遇到了问题 我还是不知道问题出在fabricjs还是php 当我发送带有彩色背景的图像时 一切正常 当我发送具有透明背景的图像时会出现问题 生成的图像是用黑色背景创建的 那么 让我更好地解释一下 当用户单
  • 使用 PixelWriter 在 JavaFX Canvas 上进行透明绘图

    有谁知道为什么使用drawImage 在Canvas上进行透明度绘制工作得很好 但在PixelWriter上却根本不起作用 我最初认为这可能与画布 上下文上的混合或其他模式 设置有关 但还没有任何运气 我需要每个像素的可变透明度 而不是整个
  • HTML Canvas:如何绘制翻转/镜像图像?

    当我在 HTML 画布上绘制图像时 我试图翻转 镜像图像 我发现一个游戏教程显示了角色必须面对的每个方向的精灵表 但这对我来说似乎不太正确 特别是因为每个框架都有不同的尺寸 实现这一目标的最佳技术是什么 我尝试致电setScale 1 1
  • 将 css 背景设置为本地存储中的图像

    我希望将 base64 格式的图像存储在密钥中的本地存储中ImgStorage在CSS背景中像这样 data image png base64 iVBORw0KGgoAAAANS 到目前为止 我尝试了两种方法 1 从存储加载并放入css标签
  • 通过鼠标和触摸在画布上绘图

    我想在画布上绘图 使用鼠标效果很好 但我必须如何修改代码才能使其在 iPad 或 Nexus 上运行 link http jsfiddle net FgNQk 6 var canvas document getElementById can
  • 从 url 加载图像并绘制到 HTML5 Canvas

    我在从 javascript 中的 url 加载图像时遇到问题 下面的代码可以工作 但我不想从 html 加载图像 我想使用纯 JavaScript 从 url 加载图像 var c document getElementById myCa
  • 有什么方法可以快速确定浏览器是否支持启用 CORS 的图像而不污染浏览器?

    是否有一个快速测试来确定浏览器是否支持启用 CORS 的图像 并且在其上绘制时不会污染画布 我知道 Chrome 15 支持此功能 Firefox 9Beta 但不支持 Firefox 8 Safari 不支持 IE9 不支持 但必须有一个
  • JavaScript 原型继承和 html canvas

    我是一名 Ruby 开发人员 最终决定认真学习 JavaScript 所以我买了一些书 开始深入研究 但当我试图理解原型继承时 我很快就陷入了困境 这本书的例子之一如下 给定一个 Shape 其原型有一个绘制方法 以及两个子形状 一个 Tr
  • 获取画布上下文的最后一个点的坐标

    我想创建一个arrowTo功能与CanvasRenderingContext2D prototype 为此 我需要获取最后一个点的坐标 例如 var ctx someCanvas getContext 2d ctx moveTo 10 40
  • 使用 javascript 调整图像大小以在画布 createPattern 中使用

    我见过一些关于如何调整图像大小的技巧 在 IMG 标签内使用 但我想在里面有一个图像变量 一个 Javascript 调整它的大小 然后使用 a 中的图像 context createPattern 图像 重复 我还没有找到任何提示 关于如
  • Arbor Js - 节点 Onclick?

    我在用着arbor js http arborjs org 创建图表 我如何创建一个onclick节点的事件 或者在单击时在某处创建节点链接 Arborjs org 主页的节点在单击时链接到外部页面 我如何复制它 或者使节点在单击时调用 j

随机推荐

  • 为什么每次进入命令都要重新source /etc/profile 才能生效?

    span style color 999988 编辑JDK8 span span style color 0086b3 export span JAVA HOME 61 span style color dd1144 34 usr java
  • MySQL配置文件my.ini的一般设置

    mysqld 设置3306端口 port 61 3306 设置mysql的安装目录 basedir 61 D Software Package mysql 8 0 12 winx64 mysql 8 0 12 winx64 设置mysql数
  • Linux shell脚本编程时bad substitution解决办法

    首先 xff0c 我们要理解bad substitution的字面意思 xff0c 它的字面意思是 替换错误 的意思 这种错误的原因呢 xff0c 通常是我们编写脚本时 和 xff08 xff09 错误使用导致的 比如应该用 xff08 x
  • Vue中的watch 和computed 属性

    之前写过一篇关于computed计算属性的文章 xff0c 详见这里 computed 内的function只执行一次 xff0c 仅当function内涉及到Vue实例绑定的data的值的改变 xff0c function才会从新执行 x
  • Swift使用XMPPFramework做IM即时通信的Demo

    上一篇文章处理了文本中表情的替换 xff0c 现在来完成消息的发送功能吧 xff08 貌似前后并没有逻辑关系哈 xff09 首先为了测试 xff0c 我们需要下载spark工具 xff0c 它可以连接openfire搭建的后台来完成即时通信
  • P1591 阶乘数码

    题目描述 求n 中某个数码出现的次数 输入格式 第一行为 t t 10 xff0c 表示数据组数 接下来 t 行 xff0c 每行一个正整数n n 1000 和数码 a 输出格式 对于每组数据 xff0c 输出一个整数 xff0c 表示 n
  • nginx缓存命中率统计(转)

    转自 xff1a http www libertyvps com thread 275 1 1 html nginx提供了 upstream cache status这个变量来显示缓存的状态 xff0c 我们可以在配置中添加一个http头来
  • windows远程桌面连接到Linux服务器(ubuntu系统)、解决xrdp登录界面port问题、解决password failed

    一 xff1a 一般在windows系统安装ssh客户端远程连接Linux服务器 xff0c 可以很方便地传输文件 xff08 注意 xff1a 文件路径不能有小括号 xff0c 空格之类的 xff0c 不然会出现erro xff09 但如
  • linux之文件系统命令

    第一章 linux之帮助命令 第二章 linux命令行快捷键 第三章 linux之防火墙 第四章 linux之服务开机自启 第五章 linux之关机与重启 第六章 linux之环境变量 第七章 linux之目录操作命令 第八章 linux之
  • 解决linux底下cmake编译使用C++ 11标准库自带的thread报错问题

    本人在编写linux底下socket编程测试服务端时候 xff0c 发现使用std thread函数时候 xff0c cmake编译通过 xff0c make编译失败 xff0c CMakeLists txt如下 xff1a cmake m
  • unity UGUI 解决ScrollView加载大量Item导致卡顿的问题

    目录 1 引言2 问题分析3 代码部分4 使用举例4 1 场景搭建4 2 测试4 3 效果展示 5 Demo下载6 结束语 1 引言 我们在平常的开发中常常碰到列表类的数据处理 xff01 典型的像玩家列表这种可能数量非常庞大 xff0c
  • 使用 Amazon EC2 启动 Windows 虚拟机

    本教程将教授您如何使用 Amazon Elastic Compute Cloud EC2 来启动 配置和连接至 Windows 虚拟机 Amazon EC2 是用于在云中创建和运行虚拟机 xff08 我们将这些虚拟机称为 实例 xff09
  • linux ss 命令用法说明

    ss 是 Socket Statistics 的缩写 ss 命令可以用来获取 socket 统计信息 xff0c 它显示的内容和 netstat 类似 但 ss 的优势在于它能够显示更多更详细的有关 TCP 和连接状态的信息 xff0c 而
  • 谷歌浏览器 跨域遇到的坑 cors 错误(亲测可行)

    浏览器版本 xff1a 一 现象 xff1a 解决方案 xff1a 方案一 xff1a xff08 已论证 xff09 步骤1 xff1a 谷歌浏览器 打开 chrome flags block insecure private netwo
  • 安装Visual Studio 2015时出现安装包丢失或损坏

    1 现象描述 在线安装vs时 xff0c 在线下载一直为0 xff0c 提示网络异常 xff0c 检查网络 xff1b 实际网络是能联网的 离线安装ISO xff0c 安装1分钟左右 提示安装包损坏或丢失 xff0c 选择从inter下载或
  • 数据库实验-数据查询练习

    用SQL语句完成以下查询 1 查询所在系为 CS 的学生学号和姓名 xff1b select sno sname from student where sdept 61 39 CS 39 2 查询选修了3号课程的学生学号 xff1b sel
  • web前端播放视频基础(多种格式,mp4, ogg, flv)(普通项目和vue项目)

    前端播放视频 满足一般播放条件 话不多说 先上代码 以下包含我能做出来可以做出来播放的视频格式 测试ok 可能还有ogg和wmv的格式可以播放 但我没测试通过 所以不展示 span class token keyword if span s
  • javascript中正则匹配多个条件, 常用正则匹配, 正则详解

    javascript中正则匹配多个条件 常用正则匹配 正则表达式常用方法实现其他的常用正则匹配详解 注意 本篇文章是根据在下日常编码过程中逐渐丰富的 越往后看 收获越丰富 收藏起来以后随时回顾 准备工作 1 汉字和unicode码的在线转换
  • antd react ProTable 基本使用

    antd react pro系列 ProTable 基本使用 一 安装二 常用字段 antd 全称 Ant Design 是目前来说运用最广泛的 react 的 ui 框架 下文就用略写 antd 代替了 pro系列不做过多解释 毕竟ui框
  • canvas插件 fabric.js 使用

    fabric js使用 fabric js 是 常用的 canvas 插件1 在项目中使用2 特殊用法 基本设置 画板数据的导入导出 遮罩 Pattern 引用官网案例 多个对象合并 并设置为 fabric 背景 适用于变色和更多场景 把