Source

Modified ago
import Music from '../music' import {$id, $class, $query, analyzeAudio, angle2rad, drawRoundedPolygon, easeInOutQuad} from '../utils' class Chino # The depth of the curves fluffiness: 20 # Size of the curves fluffSize: 8 # Initial angle angle: -17 # Number of flowers nFlowers: 64 # Particle array flowers: [] # Animation state paused: false # Blinking state blinking: false # Fully open eyesClosed: 135 # Music class for visualization player: undefined # Default frequency array frequencyArray: Array.from Array(64), -> 0 # Center of the canvas center: x: undefined y: undefined # Main circle size size: undefined # Canvas to draw on canvas: undefined ctx: undefined constructor: (@container, sliders, paused = true) -> @canvas = $query '.circles_chino' @ctx = @canvas.getContext '2d' do @resizeCanvas self = @ do (self) -> setInterval (-> do self.blink return ), 5000 # Only do these if on actual page if sliders @player = new Music @nFlowers self = @ do (self) -> window.addEventListener 'resize', -> do self.resizeCanvas # Rotate Tippy vertically sliders.verticalSlider.on 'slideChange', -> if @activeIndex is 0 self.rotateTippy -17 else self.rotateTippy 90 # Rotate Tippy horizontally sliders.horizontalSlider.on 'slideChange', -> if @activeIndex is 0 self.rotateTippy 180 else if @activeIndex is 1 self.rotateTippy 90 sliders.verticalSlider.on 'slideChangeTransitionEnd', -> if @activeIndex is 1 words = $class 'description__word_chino' for word in words do (word) -> setTimeout -> word.style.transform = 'scaleY(1)' return , 500 * do Math.random do @createFlowers @createTippy paused resizeCanvas: => @canvas.width = @container.clientWidth * 3 @canvas.height = @container.clientHeight * 2 @center.x = @canvas.width / 3 @center.y = @canvas.height / 2 @size = @container.clientWidth / 3 @fluffiness = @container.clientWidth / 120 # Main circle shouldn't be bigger than 60% of the height of the window if @size > @container.clientHeight * 0.6 @size = @container.clientHeight * 0.6 do @createFlowers return createFlowers: => # Reset flowers @flowers = [] # Create flowers for i in [0...@nFlowers] @flowers.push x: @canvas.width * do Math.random y: @canvas.height * do Math.random angle: 180 * do Math.random scale: -10 * do Math.random # negative value for delay color: if i % 2 then '255, 244, 219' else '255, 192, 203' # RGB for easy alpha opacity: 0 index: i updateFlower: (flower) => # Don't do anything until the "delay" has passed if flower.scale > 0 # Fade out once it reaches half the size if flower.scale >= 0.5 flower.opacity -= 0.004 # Fade in until it reaches half the size else flower.opacity += 0.004 # Reset flower if reached maximum size if flower.scale > 1 flower.scale = 0 flower.x = @canvas.width * do Math.random flower.y = @canvas.height * do Math.random flower.opacity = 0 # Draw flower do @ctx.beginPath @ctx.fillStyle = "rgba(#{flower.color}, #{flower.opacity})" pulse = @frequencyArray[flower.index] / 255 for i in [-1..1] by 2 @ctx.ellipse flower.x, flower.y, (30 * (1 + pulse)) * flower.scale, (50 * (1 + pulse)) * flower.scale, angle2rad(flower.angle + i * 45), 0, 2 * Math.PI do @ctx.fill # Increment size and rotation flower.scale += 0.002 flower.angle += 0.1 rotateTippy: (to) -> # Current angle start = @angle # How much he will rotate change = to - start # Half a second duration duration = 500 currentTime = 0 increment = 5 self = @ do (self) -> rotate = -> currentTime += increment val = easeInOutQuad currentTime, start, change, duration self.angle = val if currentTime < duration setTimeout rotate, increment return do rotate return drawEar: (angle) => # Draw outer part of ear # Avoid drawing black outline over already drawn areas @ctx.globalCompositeOperation = 'lighter' @ctx.fillStyle = '#FFF' do @ctx.beginPath drawRoundedPolygon @ctx, [ x: @center.x + (@size - @fluffiness/2) * Math.cos angle2rad @angle + angle - @fluffiness/4 y: @center.y + (@size - @fluffiness/2) * Math.sin angle2rad @angle + angle - @fluffiness/4 , x: @center.x + (@size + @fluffiness * 3 - @fluffiness/2) * Math.cos angle2rad @angle + angle y: @center.y + (@size + @fluffiness * 3 - @fluffiness/2) * Math.sin angle2rad @angle + angle , x: @center.x + (@size - @fluffiness/2) * Math.cos angle2rad @angle + angle + @fluffiness/4 y: @center.y + (@size - @fluffiness/2) * Math.sin angle2rad @angle + angle + @fluffiness/4 ], 10 do @ctx.stroke do @ctx.fill # Draw inner part of ear @ctx.globalCompositeOperation = 'source-over' @ctx.fillStyle = '#ee7788' do @ctx.beginPath drawRoundedPolygon @ctx, [ x: @center.x + (@size + @fluffiness/4) * Math.cos angle2rad @angle + angle - @fluffiness/8 y: @center.y + (@size + @fluffiness/4) * Math.sin angle2rad @angle + angle - @fluffiness/8 , x: @center.x + (@size + @fluffiness * 1.5 + @fluffiness/4) * Math.cos angle2rad @angle + angle y: @center.y + (@size + @fluffiness * 1.5 + @fluffiness/4) * Math.sin angle2rad @angle + angle , x: @center.x + (@size + @fluffiness/4) * Math.cos angle2rad @angle + angle + @fluffiness/8 y: @center.y + (@size + @fluffiness/4) * Math.sin angle2rad @angle + angle + @fluffiness/8 ], 5 do @ctx.fill drawEye: (angle) => @ctx.fillStyle = '#000' do @ctx.beginPath distance = @size * 0.7 # If audio is playing unless @blinking or not @player or @player.audio is undefined or @player.player.paused start = angle2rad @angle - 90 - @eyesClosed end = angle2rad @angle + @eyesClosed # Close eyes if @eyesClosed isnt 0 @eyesClosed -= 5 else # Recover form music state if not @blinking and @eyesClosed is 0 @eyesClosed = 135 # Start opening eyes if @eyesClosed is 0 @blinking = false # Close eyes if @blinking @eyesClosed -= 5 # Open eyes else if @eyesClosed isnt 135 @eyesClosed += 5 start = angle2rad @angle + 90 - @eyesClosed end = angle2rad @angle + 180 + @eyesClosed @ctx.arc @center.x + ((distance) * Math.cos(angle2rad @angle + angle)), @center.y + ((distance) * Math.sin(angle2rad @angle + angle)), @container.clientWidth / 150, start, end do @ctx.fill return blink: => # If audio isnt playing if not @player or @player.audio is undefined or @player.player.paused @blinking = true @eyesClosed = 130 return # position is either 1 or -1 drawLip: (position) => @ctx.quadraticCurveTo( @center.x + ((@size * 0.42) * Math.cos(angle2rad @angle + (-45 + position * 5))), @center.y + ((@size * 0.42) * Math.sin(angle2rad @angle + (-45 + position * 5))), @center.x + ((@size * 0.47) * Math.cos(angle2rad @angle + (-45 + position * 10))), @center.y + ((@size * 0.47) * Math.sin(angle2rad @angle + (-45 + position * 10))) ) createTippy: (paused) => if window.innerWidth >= 768 and not @paused @ctx.lineWidth = @container.clientWidth / 300 # Clear canvas @ctx.clearRect 0, 0, @canvas.width, @canvas.height # Analyze audio if @player and @player.audio isnt undefined analyzeAudio @player, @fft @frequencyArray = @player.dataArray # Draw flowers for flower in @flowers @updateFlower flower @ctx.fillStyle = '#FFF' do @ctx.beginPath # Move to right side of the body @ctx.moveTo @center.x + @size, @center.y # Draw Tippy's body (fluffy) for i in [0..360] by @fluffSize @ctx.quadraticCurveTo( @center.x + ((@size + @fluffiness) * Math.cos(angle2rad @angle + i + (@fluffSize) / 2)), @center.y + ((@size + @fluffiness) * Math.sin(angle2rad @angle + i + (@fluffSize) / 2)), @center.x + (@size * Math.cos(angle2rad @angle + i + (@fluffSize))), @center.y + (@size * Math.sin(angle2rad @angle + i + (@fluffSize))) ) do @ctx.stroke do @ctx.fill # Draw ears @drawEar -25 @drawEar -65 # Draw eyes @drawEye -25 @drawEye -65 # Nose position nose = x: @center.x + ((@size * 0.5) * Math.cos(angle2rad @angle - 45)) y: @center.y + ((@size * 0.5) * Math.sin(angle2rad @angle - 45)) radius: @container.clientWidth / 480 # Draw nose do @ctx.beginPath @ctx.arc nose.x, nose.y, nose.radius, 0, 2 * Math.PI do @ctx.fill # Mouth vertical line position line = x: @center.x + ((@size * 0.47) * Math.cos(angle2rad @angle - 45)) y: @center.y + ((@size * 0.47) * Math.sin(angle2rad @angle - 45)) @ctx.lineWidth = @container.clientWidth / 600 # Draw mouth vertical line do @ctx.beginPath @ctx.moveTo nose.x, nose.y @ctx.lineTo line.x, line.y # Draw lips @drawLip -1 @ctx.moveTo line.x, line.y @drawLip 1 do @ctx.stroke # @angle += 0.1 if paused is true @paused = true requestAnimationFrame @createTippy return export default Chino