let my={} function init(){let version='0.82' let w=520 let h=240 let s='' s+='' s+='
Drag the numerals to the blue boxes
' s+=wrap({cls:'copyrt',pos:'abs',style:'left:5px; bottom:3px'},`© 2022 Rod Pierce v${version}`) s=wrap({cls:'js',style:'width:'+w+'px; height:'+my.ht+'px;'},s) docInsert(s) my.can=new Can('canvasId',w,h,2) my.drag={n:0,onq:false,holdX:0,holdY:0} my.shapes=[] my.startX=10 my.cellWidth=40 my.spacing=10 my.drag.onQ=false this.numbers=[0,1,2,3,4,5,6,7,8,9] my.dests=[[70,100,60,40,0],[210,100,60,40,0],] my.eqs=['1','+','2','=','3'] this.ball={size:10,color:'white',lightColor:'yellow',darkColor:'blue',position:{x:160,y:120}} newGame() let el=my.can.el el.addEventListener('mousedown',mouseDownListener,false) el.addEventListener('touchstart',ontouchstart,false) el.addEventListener('mousemove',dopointer,false)} function newGame(){makeShapes() drawShapes()} function ontouchstart(ev){let touch=ev.targetTouches[0] ev.clientX=touch.clientX ev.clientY=touch.clientY ev.touchQ=true mouseDownListener(ev)} function ontouchmove(ev){let touch=ev.targetTouches[0] ev.clientX=touch.clientX ev.clientY=touch.clientY ev.touchQ=true mouseMoveListener(ev) ev.preventDefault()} function ontouchend(ev){my.can.el.addEventListener('touchstart',ontouchstart,false) window.removeEventListener('touchend',ontouchend,false) let[mouseX,mouseY]=my.can.mousePos(ev) drop(mouseX,mouseY) if(my.drag.onQ){my.drag.onQ=false window.removeEventListener('touchmove',ontouchmove,false)}} function dopointer(ev){let[mouseX,mouseY]=my.can.mousePos(ev) let inQ=false for(let i=0;ihighestIndex){my.drag.holdX=mouseX-my.shapes[i].x my.drag.holdY=mouseY-my.shapes[i].y highestIndex=i my.drag.n=i}}} if(my.drag.onQ){if(ev.touchQ){window.addEventListener('touchmove',ontouchmove,false)}else{window.addEventListener('mousemove',mouseMoveListener,false)}} if(ev.touchQ){my.can.el.removeEventListener('touchstart',ontouchstart,false) window.addEventListener('touchend',ontouchend,false)}else{my.can.el.removeEventListener('mousedown',mouseDownListener,false) window.addEventListener('mouseup',mouseUpListener,false)} if(ev.preventDefault){ev.preventDefault()} else if(ev.returnValue){ev.returnValue=false} return false} function mouseUpListener(ev){my.can.el.addEventListener('mousedown',mouseDownListener,false) window.removeEventListener('mouseup',mouseUpListener,false) let[mouseX,mouseY]=my.can.mousePos(ev) drop(mouseX,mouseY) if(my.drag.onQ){my.drag.onQ=false window.removeEventListener('mousemove',mouseMoveListener,false)}} function mouseMoveListener(ev){if(my.drag.n<0)return let[mouseX,mouseY]=my.can.mousePos(ev) let minX=0 let maxX=my.can.el.width-my.shapes[my.drag.n].wd let posX=mouseX-my.drag.holdX posX=posXmaxX?maxX:posX let posY=mouseY-my.drag.holdY my.shapes[my.drag.n].x=posX my.shapes[my.drag.n].y=posY for(let i=0;i>0)+(my.eqs[2]>>0) makeShapes() drawShapes()} function isIn(x,y,lt,tp,wd,ht){if(xlt+wd)return false if(ytp+ht)return false return true} function hitTest(shape,mx,my){if(mxshape.x+shape.wd)return false if(my>shape.y+shape.ht)return false return true} function makeShapes(){my.shapes=[] for(let i=0;i100){xp-=110 yp+=22}}} CanvasRenderingContext2D.prototype.ball=function(ball,x,y){let size=ball.size this.beginPath() this.fillStyle=ball.color this.arc(x,y,size,0,Math.PI*2,true) let gradient=this.createRadialGradient(x-size/2,y-size/2,0,x,y,size) gradient.addColorStop(0,ball.color) gradient.addColorStop(1,ball.darkColor) this.fillStyle=gradient this.fill() this.closePath() this.beginPath() this.arc(x,y,size*0.85,(Math.PI/180)*270,(Math.PI/180)*200,true) gradient=this.createRadialGradient(x-size*0.5,y-size*0.5,0,x,y,size) gradient.addColorStop(0,ball.lightColor) gradient.addColorStop(0.5,'transparent') this.fillStyle=gradient this.fill()} function docInsert(s){let div=document.createElement('div') div.innerHTML=s let script=document.currentScript script.parentElement.insertBefore(div,script)} class Can{constructor(id,wd,ht,ratio){this.id=id this.wd=wd this.ht=ht this.ratio=ratio let el=document.getElementById(id) el.width=wd*ratio el.style.width=wd+'px' el.height=ht*ratio el.style.height=ht+'px' this.g=el.getContext('2d') this.g.setTransform(ratio,0,0,ratio,0,0) this.el=el return this} clear(){this.g.clearRect(0,0,this.wd,this.ht)} mousePos(ev){let bRect=this.el.getBoundingClientRect() let mouseX=(ev.clientX-bRect.left)*(this.el.width/this.ratio/bRect.width) let mouseY=(ev.clientY-bRect.top)*(this.el.height/this.ratio/bRect.height) return[mouseX,mouseY]}} function wrap({id='',cls='',pos='rel',style='',txt='',tag='div',lbl='',fn='',opts=[]},...mores){let s='' s+='\n' txt+=mores.join('') s+={btn:()=>{if(cls.length==0)cls='btn' return '