import Canvas from 'common/canvas' ; import Ball from './ball' ; export default class FreeFallCanvas extends Canvas { ctx : CanvasRenderingContext2D ; ball : Ball ; padding : number ; lastFrameTime : number ; fps : number ; lastCalculateFpsTime : number ; isStart : boolean ; radius : number ; timer : number ; speed : number ; distance : number ; pixelPerMiter : number ; constructor ( ) { super ( ) ; this . ctx = this . getContext ( '2d' ) ; this . padding = 80 ; this . radius = 20 ; this . isStart = false ; } private init ( ) { this . fps = 0 ; this . lastFrameTime = 0 ; this . speed = 5 ; this . distance = 50 ; let pixel = this . height - this . padding * 2 ; if ( this . distance <= 0 ) { this . pixelPerMiter = 0 ; } else { this . pixelPerMiter = ( this . height - this . padding * 2 ) / this . distance ; } } private createBall ( ) { let { width , height , padding , speed , radius , pixelPerMiter , distance } = this ; this . ball = new Ball ( width / 2 , padding - radius , radius , { verticalHeight : distance , pixelPerMiter , useGravity : true , useRebound : true } ) ; this . ball . setSpeed ( speed ) ; this . ball . addBehavior ( Ball . move ) ; } private createStartButton ( ) { let { padding , radius } = this ; let btn = document . createElement ( 'button' ) ; btn . style . position = 'absolute' ; btn . style . right = radius * 2 * 1.4 + 'px' ; btn . style . top = padding - radius * 2 + 'px' ; btn . style . width = radius * 2 * 1.4 + 'px' ; btn . style . height = radius * 2 + 'px' ; btn . style . textAlign = 'center' ; btn . style . border = 'none' ; btn . style . background = 'green' ; btn . style . color = 'white' ; btn . style . borderRadius = '6px' ; btn . style . zIndex = '3' ; btn . innerText = 'start' ; ( this . el . parentElement as HTMLElement ) . style . position = 'relative' ; this . el . parentElement . appendChild ( btn ) ; btn . addEventListener ( 'click' , this . start . bind ( this ) , false ) ; } private calculateFps ( frame ) { let { fps , lastFrameTime , lastCalculateFpsTime } = this ; if ( lastFrameTime && ( fps === 0 || frame - lastCalculateFpsTime > 1000 ) ) { this . fps = 1000 / ( frame - lastFrameTime ) ; this . lastCalculateFpsTime = frame ; } } private draw ( ) { let { ctx , width , height , padding } = this ; ctx . strokeStyle = 'green' ; ctx . beginPath ( ) ; ctx . moveTo ( padding , height - padding ) ; ctx . lineTo ( width - padding , height - padding ) ; ctx . stroke ( ) ; ctx . fillStyle = 'orange' ; ctx . strokeStyle = 'red' ; this . ball . render ( ctx ) ; } private update ( ) { if ( this . fps ) { this . ball . update ( 1000 / this . fps ) ; } } private animate ( frame ) { let { width , height , ctx } = this ; ctx . clearRect ( 0 , 0 , width , height ) ; this . calculateFps ( frame ) ; this . update ( ) ; this . draw ( ) ; this . lastFrameTime = frame ; this . timer = window . requestAnimationFrame ( this . animate . bind ( this ) ) ; } public start ( ) { window . cancelAnimationFrame ( this . timer ) ; this . isStart = true ; this . ball . reset ( ) ; this . animate ( 0 ) ; } public stop ( ) { this . isStart = false ; } public render ( container ) { super . render ( container ) ; this . init ( ) ; this . createBall ( ) ; this . createStartButton ( ) ; this . draw ( ) ; } }