import '../utils/polyfill' ; import { isExpectType } from '../utils/index' ; import { name , version } from '../../package.json' ; import { ConfigType , ImgType , UniImageType } from '../types/index' ; import { defineReactive } from '../observer' ; import Watcher , { WatchOptType } from '../observer/watcher' ; export default class Lucky { protected readonly config : ConfigType ; protected readonly ctx : CanvasRenderingContext2D ; protected htmlFontSize : number = 16 ; protected rAF : Function = function ( ) { } ; protected boxWidth : number = 0 ; protected boxHeight : number = 0 ; param config constructor ( config ) { this . setHTMLFontSize ( ) ; if ( typeof config === 'string' ) config = { el : config } as ConfigType ; else if ( config . nodeType === 1 ) config = { el : '' , divElement : config } as ConfigType ; config = config as ConfigType ; this . config = config ; this . setDpr ( ) ; this . initWindowFunction ( ) ; if ( ! config . flag ) config . flag = 'WEB' ; if ( ! Object . prototype . hasOwnProperty . call ( config , 'ob' ) ) config . ob = true ; if ( config . el ) config . divElement = document . querySelector ( config . el ) as HTMLDivElement ; let boxWidth = 0 , boxHeight = 0 ; if ( config . divElement ) { config . canvasElement = document . createElement ( 'canvas' ) ; config . divElement . appendChild ( config . canvasElement ) ; } this . resetWidthAndHeight ( ) ; if ( config . canvasElement ) { config . ctx = config . canvasElement . getContext ( '2d' ) ! ; config . canvasElement . setAttribute ( 'package' , `${ name }@${ version }` ) ; config . canvasElement . addEventListener ( 'click' , e => this . handleClick ( e ) ) ; config . canvasElement . addEventListener ( 'mousemove' , e => this . handleMouseMove ( e ) ) ; config . canvasElement . addEventListener ( 'mousedown' , e => this . handleMouseDown ( e ) ) ; config . canvasElement . addEventListener ( 'mouseup' , e => this . handleMouseUp ( e ) ) ; } this . ctx = config . ctx as CanvasRenderingContext2D ; if ( ! config . ctx ) { console . error ( '无法获取到 CanvasContext2D' ) ; return ; } if ( ! this . boxWidth || ! this . boxHeight ) { console . error ( '无法获取到宽度或高度' ) ; return ; } } /**
     * 初始化方法
     */ public init ( willUpdateImgs ? ) { this . setDpr ( ) ; this . setHTMLFontSize ( ) ; this . resetWidthAndHeight ( ) ; this . zoomCanvas ( ) ; } param e protected handleClick ( e ) { } param e protected handleMouseDown ( e ) { } param e protected handleMouseUp ( e ) { } param e protected handleMouseMove ( e ) { } /**
     * 换算坐标
     */ protected conversionAxis ( x , y ) { return [ 0 , 0 ] ; } /**
     * 设备像素比
     * window 环境下自动获取, 其余环境手动传入
     */ protected setDpr ( ) { const { config } = this ; if ( config . dpr ) { } else if ( window ) { ( window as any ) . dpr = config . dpr = window . devicePixelRatio || 1 ; } else if ( ! config . dpr ) { console . error ( config , '未传入 dpr 可能会导致绘制异常' ) ; } } /**
     * 根标签的字体大小
     */ protected setHTMLFontSize ( ) { if ( ! window ) return ; this . htmlFontSize = + window . getComputedStyle ( document . documentElement ) . fontSize . slice ( 0 , - 2 ) ; } /**
     * 重置盒子和canvas的宽高
     */ private resetWidthAndHeight ( ) { const { config } = this ; let boxWidth = 0 , boxHeight = 0 ; if ( config . divElement ) { boxWidth = config . divElement . offsetWidth ; boxHeight = config . divElement . offsetHeight ; } this . boxWidth = this . getLength ( config . width ) || boxWidth ; this . boxHeight = this . getLength ( config . height ) || boxHeight ; if ( config . divElement ) { config . divElement . style . overflow = 'hidden' ; config . divElement . style . width = this . boxWidth + 'px' ; config . divElement . style . height = this . boxHeight + 'px' ; } } /**
     * 从 window 对象上获取一些方法
     */ private initWindowFunction ( ) { const { config } = this ; if ( window ) { this . rAF = ( function ( ) { return window . requestAnimationFrame || window . webkitRequestAnimationFrame || window [ 'mozRequestAnimationFrame' ] || function ( callback ) { window . setTimeout ( callback , 1000 / 60 ) ; } ; } ) ( ) ; config . setTimeout = window . setTimeout ; config . setInterval = window . setInterval ; config . clearTimeout = window . clearTimeout ; config . clearInterval = window . clearInterval ; return ; } if ( config . rAF ) { this . rAF = config . rAF ; } else if ( config . setTimeout ) { const timeout = config . setTimeout ; this . rAF = ( callback ) : number => timeout ( callback , 16.7 ) ; } else { this . rAF = ( callback ) : number => setTimeout ( callback , 16.7 ) ; } } /**
     * 根据 dpr 缩放 canvas 并处理位移
     */ protected zoomCanvas ( ) { const { config , ctx } = this ; const { canvasElement , dpr } = config ; const [ width , height ] = [ this . boxWidth * dpr , this . boxHeight * dpr ] ; const compute = ( len ) => ( len * dpr - len ) / ( len * dpr ) * ( dpr / 2 ) * 100 ; if ( ! canvasElement ) return ; canvasElement . width = width ; canvasElement . height = height ; canvasElement . style . width = `${ width }px` ; canvasElement . style . height = `${ height }px` ; canvasElement . style . transform = `scale(${ 1 / dpr }) translate(${ - compute ( width ) }%, ${ - compute ( height ) }%)` ; ctx . scale ( dpr , dpr ) ; } param src param info protected loadImg ( src , info , resolveName = '$resolve' ) { return new Promise ( ( resolve , reject ) => { if ( ! src ) reject ( `=> '${ info . src }' 不能为空或不合法` ) ; if ( this . config . flag === 'WEB' ) { let imgObj = new Image ( ) ; imgObj . src = src ; imgObj . onload = ( ) => resolve ( imgObj ) ; imgObj . onerror = ( ) => reject ( `=> '${ info . src }' 图片加载失败` ) ; } else { info [ resolveName ] = resolve ; return ; } } ) ; } param imgObj param xAxis param yAxis param width param height protected drawImage ( imgObj , xAxis , yAxis , width , height ) { let drawImg , { config , ctx } = this ; if ( [ 'WEB' , 'MP-WX' ] . includes ( config . flag ) ) { drawImg = imgObj ; } else if ( [ 'UNI-H5' , 'UNI-MP' , 'TARO-H5' , 'TARO-MP' ] . includes ( config . flag ) ) { drawImg = ( imgObj as UniImageType ) . path ; } return ctx . drawImage ( ( drawImg as CanvasImageSource ) , xAxis , yAxis , width , height ) ; } param length return protected getLength ( length ) { if ( isExpectType ( length , 'number' ) ) return length as number ; if ( isExpectType ( length , 'string' ) ) return this . changeUnits ( length as string ) ; return 0 ; } param { string } value param { number } denominator return { number } protected changeUnits ( value , denominator = 1 ) { return Number ( value . replace ( /^([-]*[0-9.]*)([a-z%]*)$/ , ( value , num , unit ) => { const unitFunc = { '%' : ( n ) => n * ( denominator / 100 ) , 'px' : ( n ) => n * 1 , 'rem' : ( n ) => n * this . htmlFontSize , } [ unit ] ; if ( unitFunc ) return unitFunc ( num ) ; const otherUnitFunc = this . config . unitFunc ; return otherUnitFunc ? otherUnitFunc ( num , unit ) : num ; } ) ) ; } param data param key param value public $set ( data , key , value ) { if ( ! data || typeof data !== 'object' ) return ; defineReactive ( data , key , value ) ; } param data param key param callback protected $computed ( data , key , callback ) { Object . defineProperty ( data , key , { get : ( ) => { return callback . call ( this ) ; } } ) ; } param expr param handler param watchOpt return protected $watch ( expr , handler , watchOpt = { } ) { if ( typeof handler === 'object' ) { watchOpt = handler ; handler = watchOpt . handler ! ; } const watcher = new Watcher ( this , expr , handler , watchOpt ) ; if ( watchOpt . immediate ) { handler . call ( this , watcher . value ) ; } return function unWatchFn ( ) { } ; } }