How can I center an object horizontally with an animation?

385 views Asked by At

I found the method centerH which puts the element correctly in the center

const activeElement = canvas.getActiveObject()
activeElement.centerH()

However it doesn't animate the object. So I tried to implement it myself

const activeElement = canvas.getActiveObject()

const { left, top } = canvas.getCenter();

const dest = {
  left: left - activeElement.width / 2,
  // top: top - activeElement.height / 2,
}

activeElement.animate(dest, {
  onChange: canvas.renderAll.bind(canvas),
  easing: fabric.util.ease['easeOutCubic'],
  duration: 500,
})

The problem here is that it doesn't work when the element is scaled and/or rotated.

Is there an easy way to create an animated centerH?

Code:

const fabric = window.fabric;

const canvas = new fabric.Canvas('canvas', {
  background: 'silver',
  centeredRotation: true,
})

canvas.setWidth(250)
canvas.setHeight(250)

const textbox = new fabric.Textbox('hello world this is', {
  left: 5,
  top: 200,
  fontSize: 30,
  hasRotatingPoint: false,
  centeredScaling: true,
  centeredRotation: true,
  originX: 'left',
  originY: 'top',
  fixedWidth: 200,
  width: 200,
  angle: -75,
}).scale(0.65)
canvas.add(textbox).setActiveObject(textbox)

// grid
const gridStyle = {
  stroke: 'red',
  strokeWidth: 1,
  selectable: false,
}
const center = {
  h: canvas.width / 2,
  v: canvas.height / 2,
}
const h = new fabric.Line([ center.h, 0, center.h, canvas.height], gridStyle)
const v = new fabric.Line([ 0, center.v, canvas.width, center.v], gridStyle)
canvas.add(h)
canvas.add(v)

// center function
document.getElementById('center').addEventListener('click', () => {
  const activeElement = canvas.getActiveObject()
  
  const { left, top } = canvas.getCenter()
  
  const dest = {
    left: left - activeElement.width / 2,
    // top: top - activeElement.height / 2,
  }
  
  activeElement.animate(dest, {
    onChange: canvas.renderAll.bind(canvas),
    easing: fabric.util.ease['easeOutCubic'],
    duration: 500,
  })
}, { passive: true })
body {
  margin: 20px;
}

canvas {
  border: 1px dashed red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.2/fabric.min.js"></script>
<button id="center">center element</button>
<br/>
<canvas id="canvas"></canvas>

1

There are 1 answers

0
shkaper On BEST ANSWER

From what I see, your task translates to

move the element to a position such that its center would be at the canvas' center

Here's a function that does the calculations. Arguments x and y are the coordinates of the point on canvas; originX and originY describe which point of your element you want to be placed at x,y. The return value is a point whose coordinates you need to set as the object's left, top to have this position, taking into consideration object's origin.

function getPositionByOrigin (x, y, originX, originY) {
  const point = new fabric.Point(x, y)
  const center = this.translateToCenterPoint(point, originX, originY)
  return this.translateToOriginPoint(center, this.originX, this.originY)
}

This is basically fabric.Object.prototype.setPositionByOrigin() but doesn't mutate the object immediately.

And here's a snippet:

const fabric = window.fabric;

const canvas = new fabric.Canvas('canvas', {
  background: 'silver',
  centeredRotation: true,
})

canvas.setWidth(250)
canvas.setHeight(250)

const textbox = new fabric.Textbox('hello world this is', {
  left: 5,
  top: 200,
  fontSize: 30,
  hasRotatingPoint: false,
  centeredScaling: true,
  centeredRotation: true,
  originX: 'left',
  originY: 'top',
  fixedWidth: 200,
  width: 200,
  angle: -75,
}).scale(0.65)
canvas.add(textbox).setActiveObject(textbox)

// grid
const gridStyle = {
  stroke: 'red',
  strokeWidth: 1,
  selectable: false,
}
const center = {
  h: canvas.width / 2,
  v: canvas.height / 2,
}
const h = new fabric.Line([ center.h, 0, center.h, canvas.height], gridStyle)
const v = new fabric.Line([ 0, center.v, canvas.width, center.v], gridStyle)
canvas.add(h)
canvas.add(v)

function getPositionByOrigin (x, y, originX, originY) {
  const point = new fabric.Point(x, y)
  const center = this.translateToCenterPoint(point, originX, originY)
  return this.translateToOriginPoint(center, this.originX, this.originY)
}

// center function
document.getElementById('center').addEventListener('click', () => {
  const activeElement = canvas.getActiveObject()
  
  const { left, top } = canvas.getCenter()
  const newPosition = getPositionByOrigin.call(activeElement, left, top, 'center', 'center')
  
  const dest = {
    left: newPosition.x,
    top: newPosition.y
  }
  
  activeElement.animate(dest, {
    onChange: canvas.renderAll.bind(canvas),
    easing: fabric.util.ease['easeOutCubic'],
    duration: 500,
  })
}, { passive: true })
body {
  margin: 20px;
}

canvas {
  border: 1px dashed red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.2/fabric.min.js"></script>
<button id="center">center element</button>
<br/>
<canvas id="canvas"></canvas>