{{{{ JavaScript - Advanced Pictures }}}}

Simple picture handing is described on another page in this website. This page describes CANVAS which allows you to draw pictures yourself. It also described MAP, an HTML feature which is useful.

CANVAS
Draw lines
Connect lines
Draw shapes
Circles, rectangles and text
Click and drag
Touch/click and drag
Touch and drag
MAP
Overlaying pictures
Multiple overlays
Start of website

Alphabetical index

Summary of colours in code: green is HTML useful for JavaScript, red is JavaScript, blue is a name and everything else is grey.


CANVAS


Draw lines

Before drawing anything, you need to define your canvas, and give it an ID and a height and width. This is done in the HTML, and the position of the canvas on the screen will be wherever you have defined it in the HTML. This code below has a button which calls a function to do something within this canvas. It is going to draw a pattern of lines, but obviously you need to click on the button to make it happen!

You need a couple of statements before you actually start doing anything. You pick up the ID of the canvas you want, and associate it with a context - in this case called 'c'. This will be attached to the various commands that we are going to use.

The code specifies what type of line that you are going to draw, in c.lineWidth and c.strokeStyle. I hope the width is obvious! The other is the colour, using the normal HTML definition. There are 6 hexadecimal digits, with the first 2 giving the red component, the next 2 the green component, and the last 2 the blue. (If you don't understand this, then '#FFFFFF' is white, '#000000' is black, '#FF0000' is red, '#00FF00' is green and '#0000FF' is blue, and each digit is one of 0,1,2,3,4,5,6,7,87,9,A,B,C,D,E,F - try experimenting!) By the way, both the line width and the colour will stay until they get changed by a new line width and colour statement.

Now to actually draw the line. You give the start point in c.moveTo. Remember this is all within your canvas. So 0,0 is top left hand corner, and the first number gives how far across, and the second how far down. Then give the end point in c.lineTo in the same way. This is the actual end point, not the distance from the start point. You still haven't drawn the line. c.stroke() does that.

The code has put all this inside a 'for' loop so you get lots of lines!




<HTML>
<HEAD>
<SCRIPT TYPE="TEXT/JAVASCRIPT">
function lines() {
  canv = document.getElementById('canvas1')
  c = canv.getContext('2d')
  c.lineWidth = 1
  c.strokeStyle = '#FF0000'
  for (i = 0; i <= 400; i = i + 10) {
    c.moveTo(0, i)
    c.lineTo(i, 400)
    c.stroke()
  }
}
</SCRIPT>
</HEAD>
<BODY>
<INPUT TYPE=BUTTON VALUE="Draw lines" ONCLICK="lines()"><BR>
<CANVAS ID="canvas1" WIDTH=400 HEIGHT=400></CANVAS>
</BODY>
</HTML>




Connect lines

The previous code draw single straight lines. This code draws lines which are joined together, and fills them. Note that the CANVAS statement sets a background colour for the canvas. This shows you where the canvas is, which might be helpful.

You must get the context of the canvas before you do anything in the canvas (or you won't be able to find it). However, here I have defined a gobal variable 'c', and set it to the context in 'stline()', which is called before anything else. That means that all other functions can use it, which cuts down on the amount of code needed! You can get the context in each function, if you prefer.

The joined together lines are a Path. This joined line is started in 'stline()'. The command beginPath() starts a new path. You might get away with leaving this out, but then all shapes will the same path, and if you change colour, then all of them will change! They are different, so the code should say so. This function also sets up line width and colour, both the outline and the filler colour.

I am making all points random, as we haven't met input yet (and I want the code to produce something different each time!) 'stline()' does the 'moveTo(x, y)' which starts the line (but doesn't actually draw it). A comment here about my data names. I have called the co-ordinates of the points 'x' and 'y'. While 'x' behaves the way that you'd expect of x co-ordinates, 'y' goes the other way. Normally, in Maths, the origin is at the bottom left corner, and 'y' goes upwards. But in JavaScript, the origin is in the top left corner, and the vertical co-ordinate goes downwards. Sorry!

'drline()' draws a line. The first time it's called, it draws from the point set up by the 'moveTo()' in 'stline()' to the current point. The next time it's called, the second line is joined on the end.

'finsh(..)' deals with both drawing an empty shape, and filling it. 'closePath()' sets up the last line, from the end of the line so far to the start point. 'stroke()' actually draws it. 'fill()' fills it in. 'stline()' starts a new line.

'cl()' clears the canvas down using 'clearRect(..)'. This clears the rectangle specified, which is the whole canvas.




<HTML>
<HEAD>
<SCRIPT TYPE="TEXT/JAVASCRIPT">
var c
function stline() {
  canv = document.getElementById('canvas2')
  c = canv.getContext('2d')
  c.beginPath()
  c.lineWidth = 3
  c.strokeStyle = '#FF0000'
  c.fillStyle = '#00FF00'
  x = rnd(400); y = rnd(400)
  c.moveTo(x, y)
}
function drline() {
  x = rnd(400); y = rnd(400)
  c.lineTo(x, y)
  c.stroke()
}
function finsh(fil) {
  c.closePath()
  c.stroke()
  if (fil==1) {c.fill()}
  stline()
}
function cl() {
  c = canv.getContext('2d')
  c.clearRect(0, 0, 400, 400)
  stline()
}
function rnd(max) {
  var rndnum = max * Math.random()
  rndnum = Math.ceil (rndnum)
  return rndnum
}
</SCRIPT>
</HEAD>
<BODY>
<INPUT TYPE=BUTTON VALUE="Draw line" ONCLICK="drline()">
<INPUT TYPE=BUTTON VALUE="Fill shape" ONCLICK="finsh(1)">
<INPUT TYPE=BUTTON VALUE="Not fill" ONCLICK="finsh(0)">
<INPUT TYPE=BUTTON VALUE="Clear" ONCLICK="cl()"> <BR>
<CANVAS ID="canvas2" WIDTH=400 HEIGHT=400 STYLE="background:#c0c0c0"></CANVAS>
<SCRIPT TYPE="TEXT/JAVASCRIPT">stline()</SCRIPT>
</BODY>
</HTML>




Draw shape

This code is similar to the previous code, but here you click in the canvas to specify the points of the shape. This is done with an ONMOUSEDOWN which calls a function, passing over a parameter of "event". This gives the co-ordinates of the click within the canvas. It can be accessed with offsetX and offsetY.

The code also puts the canvas inside a table with a border, which is another way to show where the canvas is. You don't want to be clicking outside the canvas, because nothing will happen!

This code is rather inelegant, since the first point of a new shape is not shown. I wanted to minimise the amount of code!



Click below in several places then click on button.
<HTML>
<HEAD>
<SCRIPT TYPE="TEXT/JAVASCRIPT">
var c
newshape=1
function stshape(e) {
  canv = document.getElementById('canvas3')
  c = canv.getContext('2d')
  c.beginPath()
  c.strokeStyle = '#0000FF'
  c.fillStyle = '#0000FF'
  c.moveTo(e.offsetX, e.offsetY)
  newshape=0
}
function fillshape(col) {
  c.fill()
  newshape=1
}
function drline(e) {
  if (newshape==1) {
    stshape(e)
  }
  else {
    c.lineTo(e.offsetX, e.offsetY)
    c.stroke()
  }
}
</SCRIPT>
</HEAD>
<BODY>
Click below in several places then click on button.
<INPUT TYPE=BUTTON VALUE="Fill shape" ONCLICK="fillshape()">
<BR>
<TABLE BORDER><TR><TD>
<CANVAS ID="canvas3" WIDTH=400 HEIGHT=400 ONMOUSEDOWN="drline(event)" ></CANVAS>
</BODY>
</HTML>




Circles, rectangles and text

This code just draws a picture without any input, so the code is in the BODY of the HTML.

A circle is defined using 'arc()'. The parameters are its centre (first 2 parameters), its radius, start of arc and end of arc. The first path is a full circle in yellow. Later there is a smaller half circle in red. The circle is actually drawn using 'fill()'. You could use stroke() if you just wanted an outline.

A solid rectangle is drawn with 'fillRect(..)' and an outline with 'strokeRect(..)' In each case, the parameters are top left corner (two parameters) and width and height. Note this actually draws the shape - you don't have a define then a draw instruction.

Text uses 'fillText(..)' with parameters the actual text to be displayed, and two parameters saying where it is to go. 'font' gives the font style and size.

You can stretch shapes. The code stretches a circle into an ellipse. The stretchinh cose starts with 'save()' and ends with 'restore()'. 'scale(..)' gives the scale of the stretch. There are two parameters to show which dimension is stretched. Everything between 'scale()' and 'restore()' is stretched.



<HTML>
<BODY>
<CANVAS ID="canvas4" WIDTH=400 HEIGHT=500</CANVAS>
<SCRIPT TYPE="TEXT/JAVASCRIPT">
  canv = document.getElementById('canvas4')
  c = canv.getContext('2d')

  c.beginPath()
  c.fillStyle = '#FFFF00'
  c.arc(200, 200, 150, 0, 2*Math.PI)
  c.fill()

  c.beginPath()
  c.fillStyle = '#FF0000'
  c.arc(200, 250, 50, 0, Math.PI)
  c.fill()

  c.beginPath()
  c.fillStyle = '#000000'
  c.fillRect(140, 140, 25, 25)
  c.fillRect(235, 140, 25, 25)

  c.beginPath()
  c.fillStyle = '#808000'
  c.save()
  c.scale(1, 2)
  c.arc(200, 100, 12, 0, 2*Math.PI)
  c.fill()
  c.restore()

  c.lineWidth = 3
  c.strokeStyle = '#0000FF'
  c.strokeRect(100, 400, 200, 50)

  c.fillStyle = '#008000'
  c.font = '24px sans-serif'
  c.fillText('Smile!', 160, 430)
</SCRIPT>
</BODY>
</HTML>




Click and drag

If you want the user to draw a line, then click-and-drag is an obvious way to do it. However, there are problems, because various browsers treat click-and-drag differently, assuming that it means different things. It gets quite complicated! You also get the distinction between click-and-drag and touch-and-drag (for touch sensitive screens).

The following examples all use CANVAS. My normal simple graphics technique of using small pictures (to represent mosaic tiles, for example) doesn't work with click-and-drag, or at least I haven't found out how! Once you click on an element (such as a picture), the system seems to get fixated on that element, and doesn't react as you might expect if you keep the mouse or touch down while moving to a different element. It assumes you want to actually drag it....

This code draws a line using click-and-drag (i.e. using a mouse). Click and drag in the white area. (This won't work if you don't have a mouse.)



<HTML>
<HEAD>
<SCRIPT>
var c5

function stline(e) {
canv = document.getElementById('canvas5')
c5 = canv.getContext('2d')
c5.beginPath()
c5.strokeStyle = '#0000FF'
c5.moveTo(e.offsetX, e.offsetY)
}

function drline(e) {
if (e.buttons==0) {return}
c5.lineTo(e.offsetX, e.offsetY)
c5.stroke()
}
</SCRIPT>
</HEAD>

<BODY>
<CANVAS ID="canvas5" WIDTH=400 HEIGHT=500
   ONMOUSEDOWN="stline(event)"
   ONMOUSEMOVE="drline(event)"
>
</CANVAS>

</BODY>
</HTML>

"stline" starts the line, and happens when the mouse button is pressed down (ONMOUSEDOWN). "drline" draws the line and happens while the mouse is being moved (ONMOUSEMOVE).

The code should work in Internet Explorer and Firefox. It should also work in Edge, and there is one specific statement to allow that: "e.preventDefault()". If you don't have this, then Edge sometimes assumes that your click-and-drag is picking up the whole page and moving it around. It kindly tells you that you're not allowed to do that, by which time, your code is in a muddle! (It doesn't always do this, which sounds like an Edge bug. But it does it often enough to be annoying!) So that statement prevents this.

"if (e.buttons == 0) {return}" at the start of "drline" is necessary, because ONMOUSEMOVE is activated whenever the mouse moves, whether the mouse button is pressed down or not. "e.buttons" is zero when no buttons are pressed down.

In the CANVAS statement, you can also have ONMOUSEUP (finger lifted off mouse) and ONMOUSEOUT (mouse leaves the CANVAS area).





Touch/click and drag

The code above deals with a mouse click-and-drag, but many devices now have touch sensitive screens, and don't have a mouse. (Some have both, of course.) Here is code for touch-and-drag. Touch (or click) and drag in the white area.



<HTML>
<HEAD>
<SCRIPT>
var c6

function stlinep(e) {
canv = document.getElementById('canvas6')
c6 = canv.getContext('2d')
c6.beginPath()
c6.strokeStyle = '#0000FF'
c6.moveTo(e.offsetX, e.offsetY)
}

function drlinep(e) {
if (e.pointerType == "mouse" && e.buttons==0) {return}
c6.lineTo(e.offsetX, e.offsetY)
c6.stroke()
}
</SCRIPT>
</HEAD>

<BODY>
<CANVAS ID="canvas6" WIDTH=400 HEIGHT=500
   STYLE="touch-action:none"
   ONPOINTERDOWN="stlinep(event)"
   ONPOINTERMOVE="drlinep(event)"
>
</CANVAS>

</BODY>
</HTML>

The commands ONPOINTERDOWN and ONPOINTERMOVE work for both mouse and touch. However, a friend found that this won't work with her iPad (grr!!), so see below for code that will.

There is one thing to think about. ONMOUSEMOVE recognises how the mouse cursor is moving, whether or not the button is pressed down. But touch can only be recognised when you actually touch the screen! Yes, this is obvious, but it didn't occur to me at first! So the code "if (e.buttons == 0) {return}" is needed for the mouse. But it mustn't be actioned for the touch code, which doesn't know anything about 'e.buttons', so we need the additional condition 'e.pointerType == "mouse"' to stop this.





Touch and drag

This is code specifically for Touch (and it should work on the iPad). It won't work with a mouse, but you could combine code for click-and-drag with this. Touch and drag in the white area.



<HTML>
<HEAD>
<SCRIPT>
var c7
var rect

function stlinet(e) {
canv = document.getElementById('canvas7')
c7 = canv.getContext('2d')
touch = e.touches[0]
rect = canv.getBoundingClientRect();
x = Math.floor(touch.clientX - rect.left)
y = Math.floor(touch.clientY - rect.top)
c7.beginPath()
c7.strokeStyle = '#0000FF'
c7.moveTo(x, y)
}

function drlinet(e) {
touch = e.touches[0]
x = Math.floor(touch.clientX - rect.left)
y = Math.floor(touch.clientY - rect.top)
c7.lineTo(x, y)
c7.stroke()
}
</SCRIPT>
</HEAD>

<BODY>
<CANVAS ID="canvas7" WIDTH=400 HEIGHT=500
   ONTOUCHSTART="stlinet(event)"
   ONTOUCHMOVE="drlinet(event)"
>
</CANVAS>

</BODY>
</HTML>

It uses ONTOUCHSTART and ONTOUCHMOVE rather than POINTER or MOUSE. There is one problem - getting the x and y value of actually where you have touched. You can't get x and y within the CANVAS. But you can get the x and y within the screen, and you can get where the CANVAS is on the screen, then take one away from the other.

Look, I can't guarantee that this code, and the ones above, actually work! I've been tearing my hair out to find this code, and it seems to work on my devices. But sorry if I've slipped up!




MAP


This is actually HTML rather than JavaScript, but it is very useful, and can be combined with JavaScript. HTML allows you to display a picture using IMG, and it is possible to click on the picture to make things happen (see simple pictures). MAP allows you to define parts of the picture, so clicking in one part of the picture does one thing, but clicking on another does something different.

First, you have to define a map. This defines a number of areas within a larger space. All co-ordinants are measured in pixels, and they come in pairs. Starting from the top left, a pair of co-ordinants gives how far across the point is, and how far down (in that order). To define a rectangle within the larger map, give two pairs of co-ordinants, for the top left point, and the bottom right point.

But a map is no good by itself. So then, when giving the IMG for the picture, you have to say which map you are using.



<HTML>
<BODY>

<MAP NAME=mymap>
<AREA COORDS="
35,30,70,60" ONMOUSEDOWN="alert('red')">
<AREA COORDS="
145,80,180,110" ONMOUSEDOWN="alert('blue')">
</MAP>

<IMG SRC="mappic.gif" USEMAP="#mymap">
</BODY>
</HTML>

The code above does a different 'alert' depending where you click on it.

The default shape for AREA is a rectangle (SHAPE=RECT). You can also do SHAPE=POLY. The COORDS will still be pairs, but each pair describes the corner of a polygon. So there will be as many pairs of coordinates as corners.




Overlaying pictures


This HTML code allows you to overlay one picture on top of another. That would obliterate the bottom picture of course, unless the top picture has transparent pieces in it.




<HTML>
<HEAD>
<STYLE>
.back {
background-image: url("
overlay1.gif");
}
</STYLE>

</HEAD>
<BODY>
<IMG SRC="overlay1.gif">
<IMG SRC="overlay2.gif">
<IMG CLASS="back" SRC="overlay2.gif">
</BODY>
</HTML>

One picture has an A on it, and the other a B. These are displayed separately. Then one is displayed on top of the other.



Multiple overlays

This allows more than 2 layers, and being able to change the middle layer.



Hover mouse over a letter:

<HTML>
<BODY>
<DIV STYLE="background-image: url('circle.gif'); background-repeat: no-repeat" ID="lev1">
   DIV STYLE="background-image: url('a.gif'); background-repeat: no-repeat" ID="lev2">
    <DIV STYLE="background-image: url('bar.gif'); width:240px; height:240px" ID="lev3">
    </DIV>
  </DIV>
</DIV>
<H2>Hover mouse over a letter:</H2>
<IMG SRC="b.gif" ONMOUSEOVER="lev2.style.backgroundImage='url(b.gif)'" ONMOUSEOUT="lev2.style.backgroundImage='url(a.gif)'">
<IMG SRC="c.gif" ONMOUSEOVER="lev2.style.backgroundImage='url(c.gif)'" ONMOUSEOUT="lev2.style.backgroundImage='url(a.gif)'">
</BODY>
</HTML>


Return to JavaScript index



© Jo Edkins 2019