Course notes 33
syllabus | schedule | exercises | assignments | class notes | resources | students | ARTC courses

User Interface Elements - Slider and Dial

ctx.drawImage(image,x,y,w,h);

The basic drawImage function allows one to place an entire image into a canvas at the specified coordinates. You can also specify the width and height of the image. This allows for some functionality, but becomes very limiting when trying perform more complex image operations.

ctx.drawImage(image, sliceX, sliceY, sliceWidth, sliceHeight, destinationX, destinationY, destinationWidth, destinationHeight)

The advanced drawImage function allows finer manipulation on what is being drawn and where it is placed.

  • image→ the reference to the image being drawn from
  • sliceX→ the x position on the image at which to start copying the image
  • sliceY→ the y position on the image at which to start copying the image
  • sliceWidth→ the total width to be copied from the image
  • sliceHeight→ the total height to be copied from the image
  • destinationX→ the x position on the canvas at which to start drawing
  • destinationY→ the y position on the canvas at which to start drawing
  • destinationWidth→ the total width to drawn on the canvas
  • destinationHeight→ the total height to draw on the canvas

iOS Slider

The below canvas-based slider is constructed from a single image by selectively drawing different portions:

var sliderSprite = new Image();
sliderSprite.src =  "data:image/png;base64,iVBORw...."

var slider,sliderCTX, sliderPos = 0;

function  init(){
    slider = document.getElementById('slider');
    sliderCTX = slider.getContext('2d');
    document.getElementById('sliderIMG').src = sliderSprite.src;
    drawSlider(slider,sliderCTX);
    
    slider.addEventListener('mousedown',sliderDown,false);
    slider.addEventListener('mouseup',sliderUp,false);
}

function findOffset(obj) {
    var curX = curY = 0;
    if (obj.offsetParent) {
        do {
            curX += obj.offsetLeft;
            curY += obj.offsetTop;
        } while (obj = obj.offsetParent);
    return {x:curX,y:curY};
    }
}

function sliderDown(e){
    var pos = findOffset(slider);
    var mouseX = e.pageX - pos.x;
    
    if(mouseX > sliderPos && mouseX < sliderPos+22){
        slider.addEventListener('mousemove',sliderMove,false);
    }
}
function sliderUp(e){
    slider.removeEventListener('mousemove',sliderMove,false);
}

function sliderMove(e){
    var pos = findOffset(slider);
    var mouseX = e.pageX - pos.x;
    if(mouseX-10 > 0 && mouseX+10 < slider.width){
        sliderPos = mouseX-11;
        drawSlider(slider,sliderCTX);
    }
}


function drawSlider(canvas,ctx){
    ctx.clearRect(0,0,canvas.width,canvas.height);
    //draw gray bar
    ctx.drawImage(sliderSprite,20,24,1,9,9,8,canvas.width-18,9);
    //draw right cap
    ctx.drawImage(sliderSprite,10,24,9,9,canvas.width-9,8,9,9);
    // draw left cap
    ctx.drawImage(sliderSprite,0,24,9,9,0,8,9,9);
    // draw blue bar
    if(sliderPos > 9){
        ctx.drawImage(sliderSprite,22,24,1,9,9,8,sliderPos,9);
    }
    // draw control button
    ctx.drawImage(sliderSprite,0,0,22,22,sliderPos,1,22,22);
document.getElementById('output').innerHTML=Math.floor(sliderPos/(canvas.width-22) * 100);
}
    

Rotating Dial

Similar to the slider, the dial is composed from a single image.

This time the image is used in combination with normal path drawing commands. To achieve the rotational movement, the mouse position is used to calculate the angle at which to rotate the context before drawing the needle and dial.

var dialSprite = new Image();
dialSprite.src = "data:image/png;base64,iVBORw..."

var dial, dialCTX, dialPos = 0;

function  init(){
    dial = document.getElementById('dial');
    dialCTX = dial.getContext('2d');
    document.getElementById('dialIMG').src = dialSprite.src;
    drawDial(dial,dialCTX,0);	
    
    dial.addEventListener('mousedown',dialDown,false);
    dial.addEventListener('mouseup',dialUp,false);
    
}

function findOffset(obj) {
    var curX = curY = 0;
    if (obj.offsetParent) {
        do {
            curX += obj.offsetLeft;
            curY += obj.offsetTop;
        } while (obj = obj.offsetParent);
    return {x:curX,y:curY};
    }
}

function dialDown(e){
    console.log('dialDown');
    dial.addEventListener('mousemove',dialMove,false);
}
function dialUp(e){
    console.log('dialUp');
    dial.removeEventListener('mousemove',dialMove,false);
}
function dialMove(e){
    console.log('dialMove');
    var pos = findOffset(dial);
    var mouseX = e.pageX - (pos.x+75); 
                       // since we are translating the center to (75,75) before drawing
    var mouseY = e.pageY - (pos.y+75); 
                       // the needle and dial, get the post relative to that
    
    if(mouseY < 0) { // only care about movement on the dial
        var angle = Math.atan(mouseY / mouseX);  
                     //calculate the angle to rotate to -> SOHCAHTOA 
        
        if(mouseX > 0){ // if on the right side of the dial
            angle = Math.PI - Math.atan(-mouseY / (mouseX*2));  
                         //subtract the angle from Math.PI to get the needed angle 	
        }
        
        drawDial(dial,dialCTX,angle);
    }
    
}

function drawDial(canvas,ctx,angle){
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.drawImage(dialSprite,0,0,150,78,0,0,150,78);
    ctx.save();
    ctx.translate(75,75);
    ctx.rotate(angle);
    
    ctx.fillStyle = 'red';
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.lineTo(0,-5);
    ctx.lineTo(-60,0);
    ctx.lineTo(0,5);
    ctx.fill();

    ctx.drawImage(dialSprite,0,92,57,57,-28,-28,57,57);
    ctx.restore();
}