Optical illusion with a circle of circles
- Experiment
- Vue.js
Hover (click on mobile) the graph to see each circle’s path.
A optical illusion occurs when a series of circles moving in straight lines. After seeing a YouTube video with the same illusion, I wanted to challenge myself to make it in d3.js.
In the code below you’ll see that I’m using d3’s easeSinInOut
method to achieve this animation.
import { select, easeSinInOut } from 'd3'
const size = 750
const count = 16
const duration = 3000
const radius = 22
const padding = radius
const data = [...Array(count).keys()]
export default class Graph {
constructor(svg) {
this.svg = select(svg).attr('viewBox', [-padding, -padding, size + padding * 2, size + padding * 2].join(' '))
this.render()
}
render() {
let g = this.svg
.selectAll('g')
.data(data)
.join((enter) => {
let g = enter.append('g')
g.append('rect')
g.append('circle')
return g
})
this.svg
.on('mouseenter', () => {
g.select('circle').attr('opacity', 0.25)
g.select('rect').attr('opacity', 0.1)
const group = g.filter((d, i) => i === 0 || i === count / 2)
group.select('circle').attr('opacity', 1)
group.select('rect').attr('opacity', 1)
})
.on('mouseleave', () => {
g.select('circle').attr('opacity', 1)
g.select('rect').attr('opacity', 0)
})
g.attr('transform', (d) => {
const degrees = ((d / count) * Math.PI * 180) / Math.PI
return `translate(${size / 2}, ${size / 2}) rotate(${degrees})`
})
g.select('rect')
.attr('fill', 'white')
.attr('height', 2)
.attr('width', size)
.attr('opacity', 0)
.attr('transform', `translate(${-size / 2}, 0)`)
const circle = g
.select('circle')
.attr('r', radius)
.attr('fill', '#FFD650')
.attr('cx', -size / 2)
.attr('transform', `translate(0,0)`)
circle.each(function (d, i) {
repeat(select(this), i, true)
})
function repeat(el, i, first) {
el.transition()
.ease(easeSinInOut)
.duration(duration)
.attr('transform', `translate(${size},0)`)
.delay(() => (first ? i * (duration / count) : 0))
.transition()
.ease(easeSinInOut)
.duration(duration)
.attr('transform', `translate(0,0)`)
.on('end', () => {
repeat(el, i, false)
})
}
}
}