【问题标题】:FabricJS - Continues rotation on object rotation eventFabricJS - 在对象旋转事件上继续旋转
【发布时间】:2018-04-06 18:55:25
【问题描述】:

我在使用 object:rotating 事件函数时遇到了元素的奇怪行为。元素以一些额外的角度连续旋转。看起来角度一直累积到前一个角度...

基本上,我试图实现的是旋转红色元素时黄色元素的适当位置和角度。

这里是jsFiddle。请尝试旋转红色元素。

var canvas = this.__canvas = new fabric.Canvas('paper',{
                preserveObjectStacking: true
            });

const data = {
    red:{
    	top: 50,
        left: 50,
        angle: -30,
        width: 300,
        height: 200
    },
	yellow:{
    	top: 50,
        left: 50,
        angle: 20,
        width: 100,
        height: 100
    }
};
const yellow = new fabric.Rect({
    left: data.yellow.left, 
    top:  data.yellow.top,
    fill: 'yellow',
    angle:  data.yellow.angle,
    width: data.yellow.width,
    height: data.yellow.height,
    originX: 'center',
    originY: 'center',
    opacity: .7
});

yellow.setPositionByOrigin({x:yellow.getLeft() + yellow.getWidth() / 2, y: yellow.getTop() + yellow.getHeight() / 2}, 'center', 'center');

fabric.Image.fromURL('//images.sftcdn.net/images/t_optimized,f_auto/p/85e6f558-9a68-11e6-bdf1-00163ed833e7/1312338326/slack-logo.png', function(img) {
    const patternSourceCanvas = new fabric.StaticCanvas();
    patternSourceCanvas.enableRetinaScaling = false;
    patternSourceCanvas.setBackgroundColor('red', patternSourceCanvas.renderAll.bind(patternSourceCanvas));
    patternSourceCanvas.setBackgroundImage(img, patternSourceCanvas.renderAll.bind(patternSourceCanvas), {
        top: yellow.getTop() - yellow.getWidth() / 2,
        left: yellow.getLeft() - yellow.getHeight() / 2,
        width: yellow.getWidth(),
        height: yellow.getHeight(),
        angle: yellow.getAngle(),
        scaleX: 1,
        scaleY: 1,
        originX: 'center',
        originY: 'center',
    });

    var pattern = new fabric.Pattern({
        source: function() {
            patternSourceCanvas.setDimensions({
                width: data.red.width,
                height: data.red.height
            });
            patternSourceCanvas.renderAll();
            return patternSourceCanvas.getElement();
        },
        repeat: 'no-repeat'
    });

    const red = new fabric.Rect({
        left: data.red.left,
        top: data.red.top,
        angle: data.red.angle,
        fill: pattern,
        width: data.red.width,
        height: data.red.height,
        originX: 'center',
        originY: 'center',
        patternSourceCanvas: patternSourceCanvas
    });
	red.setPositionByOrigin({x:red.getLeft() + red.getWidth() / 2, y: red.getTop() + red.getHeight() / 2}, 'center', 'center');
    
    canvas.add(red);
    canvas.add(yellow);
   
    canvas.on('object:rendered', function(){
		console.log('Rednder')
    });
    
    red.on('rotating', function(){
	    updateYellowPosition();
    });
    red.on('moving', function(){
	    updateYellowPosition();
    });
   
    updateYellowPosition();
   
   
    function updateYellowPosition(){
    	// Here I would like to have some logic which will identify proper position
        // (left, top and angle) of Red's background image location and apply those 
        // to the Yellow element
        console.log('Yellow left top', red, red.angle, red.getAngle(), yellow.getLeft(), yellow.getTop());
        const shiftX = red.patternSourceCanvas.backgroundImage.left;
        const shiftY = red.patternSourceCanvas.backgroundImage.top;
        const backgroundAngle = red.patternSourceCanvas.backgroundImage.angle;
        const redCenterPoint = red.getCenterPoint();
        const newYellowPos = fabric.util.rotatePoint(
            new fabric.Point( 
            	yellow.getLeft(), 
				yellow.getTop()
            ), // NOT SURE
            new fabric.Point(
            	redCenterPoint.x, 
                redCenterPoint.y
            ), // NOT SURE
            fabric.util.degreesToRadians(red.getAngle())  // NOT SURE
        );  
        
        yellow.setPositionByOrigin (
        	{
            	x: newYellowPos.x,
	            y: newYellowPos.y
	        },
            'center',
            'center'
        );

        yellow.rotate(yellow.getAngle() + red.getAngle()).setCoords();
       	canvas.renderAll();
    }

},{crossOrigin: 'Anonymous'});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
<canvas id="paper" width="400" height="400" style="border:1px solid #ccc"></canvas>

【问题讨论】:

  • 如果你把它们做成一个组有什么问题吗?

标签: canvas rotation fabricjs


【解决方案1】:

此示例向您展示如何在 fabricJS 中以通用方式处理转换。

您应该从红色矩形的中心找到相对位置。 将其保存在变量中。

每个变换步骤,计算红色矩形变换矩阵,并使用该矩阵确定画布中现在的相对位置。

将黄色矩形放置在那里并给它一个直角。

我更新了示例以使用 scaleX 和 scaleY。

如果您需要,它应该也可以与倾斜一起使用。

Matrix 可以转换 2d 画布所能做的所有事情,因此您应该学习如何使用它们,它们通常提供解决方案和代码快捷方式

var canvas = this.__canvas = new fabric.Canvas('paper',{
                preserveObjectStacking: true
            });

const data = {
    red:{
    	top: 50,
        left: 50,
        angle: -30,
        width: 300,
        height: 200
    },
	yellow:{
    	top: 50,
        left: 50,
        angle: 20,
        width: 100,
        height: 100
    }
};
const yellow = new fabric.Rect({
    left: data.yellow.left, 
    top:  data.yellow.top,
    fill: 'yellow',
    angle:  data.yellow.angle,
    width: data.yellow.width,
    height: data.yellow.height,
    originX: 'center',
    originY: 'center',
    opacity: .7
});

yellow.setPositionByOrigin({x:yellow.getLeft() + yellow.getWidth() / 2, y: yellow.getTop() + yellow.getHeight() / 2}, 'center', 'center');

fabric.Image.fromURL('//images.sftcdn.net/images/t_optimized,f_auto/p/85e6f558-9a68-11e6-bdf1-00163ed833e7/1312338326/slack-logo.png', function(img) {
    const patternSourceCanvas = new fabric.StaticCanvas();
    patternSourceCanvas.enableRetinaScaling = false;
    patternSourceCanvas.setBackgroundColor('red', patternSourceCanvas.renderAll.bind(patternSourceCanvas));
    patternSourceCanvas.setBackgroundImage(img, patternSourceCanvas.renderAll.bind(patternSourceCanvas), {
        top: yellow.getTop() - yellow.getWidth() / 2,
        left: yellow.getLeft() - yellow.getHeight() / 2,
        width: yellow.getWidth(),
        height: yellow.getHeight(),
        angle: yellow.getAngle(),
        scaleX: 1,
        scaleY: 1,
        originX: 'center',
        originY: 'center',
    });

    var pattern = new fabric.Pattern({
        source: function() {
            patternSourceCanvas.setDimensions({
                width: data.red.width,
                height: data.red.height
            });
            patternSourceCanvas.renderAll();
            return patternSourceCanvas.getElement();
        },
        repeat: 'no-repeat'
    });

    const red = new fabric.Rect({
        left: data.red.left,
        top: data.red.top,
        angle: data.red.angle,
        fill: pattern,
        width: data.red.width,
        height: data.red.height,
        originX: 'center',
        originY: 'center',
        patternSourceCanvas: patternSourceCanvas
    });
	red.setPositionByOrigin({x:red.getLeft() + red.getWidth() / 2, y: red.getTop() + red.getHeight() / 2}, 'center', 'center');
  
  // you are working with center coordinates
// find the distance between the 2 centers and the angle difference.
var angleDiff = yellow.angle; // the image to cover is angled as yellow angle, relative to red angle
var centerDiff = yellow.getCenterPoint().subtract(red.getCenterPoint());
    
    canvas.add(red);
    canvas.add(yellow);
   
    canvas.on('object:rendered', function(){
		console.log('Rednder')
    });
    
    red.on('rotating', function(){
	    updateYellowPosition();
    });
    red.on('moving', function(){
	    updateYellowPosition();
    });
    red.on('scaling', function(){
	    updateYellowPosition();
    });
   
    updateYellowPosition();
   
   
    function updateYellowPosition(){
    	// Here I would like to have some logic which will identify proper position
        // (left, top and angle) of Red's background image location and apply those 
        // to the Yellow element
        var matrix = red.calcTransformMatrix(); // get all the position information of red
        var newCenterForYellow = fabric.util.transformPoint(centerDiff, matrix);
        yellow.top = newCenterForYellow.y;
        yellow.left = newCenterForYellow.x;
        yellow.angle = red.angle + angleDiff;
         yellow.scaleX = red.scaleX;
         yellow.scaleY = red.scaleY;
       	canvas.renderAll();
    }

},{crossOrigin: 'Anonymous'});
canvas {

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
<canvas id="paper" width="400" height="400" style="border:1px solid #ccc"></canvas>

【讨论】:

  • 谢谢你,安德里亚。工作好!我需要缩放的一些不同的行为,但是对于开始来说已经足够了......基本上,对于红色的缩放,我需要更改它的高度和宽度,但始终保持缩放 1,所以调整大小而不是缩放...您认为您的代码可能支持这种行为吗?
  • 我检查了缩放...不能正常工作...尝试垂直缩放红色,例如垂直缩放,您会注意到黄色得到错误的转换。
  • 是的,如果你从一侧扩展,我猜你必须添加倾斜支持。多一点代码。
  • tnx.还有一个问题……你现在能帮我解决黄色问题吗?现在我需要移动一个黄色元素并在红色图案上应用适当的位置......你能帮我吗?你对所有这些转换功能都很好:)
  • @EugeneVilder 如果你想移动,相对于彼此。将所有对象组成一组。
猜你喜欢
  • 2020-07-04
  • 1970-01-01
  • 2021-09-13
  • 2013-12-26
  • 1970-01-01
  • 2013-04-22
  • 1970-01-01
  • 2014-12-28
  • 1970-01-01
相关资源
最近更新 更多