Creating masterpieces with raphael

  • 82 views
Uploaded on

A hands on experience of using the Raphael Javascrpt library

A hands on experience of using the Raphael Javascrpt library

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
82
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
1
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Creating Masterpieces with Raphaël A journey with a JavaScript library Maurice Maneschi http://redwaratah.com
  • 2. Outline Introducing Raphaël Making a game board Making the pieces Making the controls Demo Where to next Resources
  • 3. What is Raphaël A small JavaScript library Manage vector graphics in web browsers Easy to use and cross platform Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ and Internet Explorer 6.0+ “It is not supposed to replace Flash, but it probably does in many cases”
  • 4. Goal
  • 5. What will I show A 1970's board game moved into a browser How I solved the various drawing issues Lots of Raphaël code
  • 6. What I won't show I used jQuery, AJAX, PHP and MySQL to manage the infrastructure Design decisions on the interface I can't release the full code as it might breach copyright laws I can answer questions on these afterwards
  • 7. W ARNING THIS PRESENTATION MAY CONTAIN TRACES OF TRIGONOMETRY. VIEW DISCRETION IS ADVISED ER
  • 8. Scalable Vector Graphics an image format implemented in XML allows precise images to be described can be scaled, rotated etc without loss of quality HTML 5 supports SVG images Modern browsers support it differently Dmitry Baranovskiy had bad experiences with these differences so created Raphaël to abstract away the special cases
  • 9. Samples – on the Raphaël site
  • 10. Graphics have layers
  • 11. Graphics have Layers - 2 Important to manage your layers Raphaël does not have layers per-se insertBefore insertAfter Use sets to keep it together If you use toFront or toBack, you are doing it wrong (I have a couple in the current code from debugging)
  • 12. Making a Game Board Layer 1 Hexagons to regular movement Different terrain that impacts movement and combat Different for each game
  • 13. Drawing a hexagon var paper = new Raphael("holder", 200, 200); var path = paper.path(' M 0 86.6 L 50 173.2 150 173.2 200 86.6 150 0 50 0 Z') .attr({'stroke: '#000'});
  • 14. Drawing Lots of Hexagons 1 2 3 4
  • 15. Drawing lots of Hexagons while (line < this.HEIGHT) { var x = 0, y = line, s = 'M0 ' + line + 'L'; while (x < this.WIDTH) { x += this.HEX_SIDE * this.COS60; y += this.HEX_SIDE * this.SIN60 * (even ? -1 : 1); s += ' ' + x + ' ' + y; if (x >= this.WIDTH) break; x += this.HEX_SIDE; if (!even || y == 0) { s += ' ' + x + ' ' + y; } else { s += 'M' + x + ' ' + y + 'L'; } if (x >= this.WIDTH) break; x += this.HEX_SIDE * this.COS60; y += this.HEX_SIDE * this.SIN60 * (even ? 1 : -1); s += ' ' + x + ' ' + y; if (x >= this.WIDTH) break; x += this.HEX_SIDE; if (even) { s += ' ' + x + ' ' + y; } else { s += 'M' + x + ' ' + y + 'L'; } } if (!even) line += this.HEX_SIDE * this.SIN60 * 2; even = !even; this.paper.path(s).attr(attr); }
  • 16. Drawing Rivers Rivers are paths with a start and finish You do not want to touch neighbouring cells You want to have a bit of randomness Raphaël has many different curve options C = Curve to S = Smooth curve to Q = Quadratic Bezier curve to T = Smooth quadratic Bezier curve to, etc. Experiment!
  • 17. “We can always count on the Americans to do the right thing, after they have
  • 18. Drawing Rivers - 2 $.each(source, function (idx, path) { // an array of rivers var first = new $.Cell(path[0]); var mid = first.getHexMidpoint("original"), lastMid; var line = "M" + mid.x + ' ' + mid.y + 'S'; var i = 1; game[target].push(first); while (i < path.length) { lastMid = mid; var next = new $.Cell(path[i]); mid = next.getHexMidpoint("original"); game[target].push(next); line += ' ' + Raphael.format('{0} {1} {2} {3}', (mid.x + lastMid.x) / 2, (mid.y + lastMid.y) / 2, mid.x, mid.y); i++; } game.paper.path(line).attr(attr); });
  • 19. Drawing Forests Forests are closed paths that are filled in You need to find the boundaries and walk around them No colour in the hexes next to the forest Not perfectly regular
  • 20. Drawing Forests - 2 A Failed Attempt
  • 21. Drawing Forests - 3 3 1 2 4
  • 22. Drawing Forests - 4 // Find a forest cell next to a non forest cell var forestCell, clearCell, s, direction; forest.each(function(idx, cell) { var neighbours = cell.getAdjacentCells(); direction = -1; $.each(neighbours, function(idx, adjacentCell) { if (!adjacentCell) return; var gotOne = forest.indexOf(adjacentCell); if (gotOne >= 0) return; // Looking for a clear cell direction = idx; forestCell = cell; clearCell = adjacentCell; return false; }); if (direction == -1) return; // cell is surrounded by forest cells, so not needed for drawing return false; // got one! }); var mf = forestCell.getHexMidpoint("original"), mc = clearCell.getHexMidpoint("original"); s = "M" + Math.round((3 * mc.x + 5 * mf.x)/8,2) + ' ' + Math.round((3 * mc.y + 5 * mf.y)/8,2) + 'S'; var countClear = 0; var firstForest = forestCell, firstClear = clearCell;
  • 23. Drawing Forests - 5 while (countClear < 6) { direction = (direction + 1) % 6; // step one clockwise var nextCell = forestCell.getAdjacentCells()[direction]; var gotOne = forest.indexOf(nextCell); var mn = nextCell.getHexMidpoint("original"); if (gotOne >= 0) { var midX = Math.round((10 * (mf.x + mn.x)/2 + 3 * mc.x)/13,2), midY = Math.round((10 * (mf.y + mn.y)/2 + 3 * mc.y)/13,2); s += ' ' + midX + ' ' + midY; countClear = 0; forestCell = nextCell; mf = mn; direction = forestCell.directionTo(clearCell); } else if (game.forests.contains(nextCell)) { break; // Gone around and completed the loop. Beware a clearing in the forest though } else { countClear += 1; clearCell = nextCell; mc = mn; } var scale = (countClear % 2) ? {c: 3, f: 5} : {c: 3, f:6}; s += " " + Math.round((scale.c * mc.x + scale.f * mf.x)/(scale.c + scale.f),2) + ' ' + Math.round((scale.c * mc.y + scale.f * mf.y)/(scale.c + scale.f),2); if (forestCell.equals(firstForest) && clearCell.equals(firstClear)) break; // We have cirled the copse if (direction == -1) break; // algorithm failure } game.paper.path(s + 'z').attr({ stroke: "#060", fill: "#090", "stroke-width": 1, "stroke-linejoin": "round" });
  • 24. Drawing Villages Four cubes At different orientations At different locations Seed the random number generator to the hex index So a redraw does not alter the buildings!
  • 25. Drawing Slopes Want a fan effect Longer if end slope, shorter if next to another slope Not too regular A lot of experimentation
  • 26. Drawing Slopes - 2 Why longer and shorter
  • 27. Drawing Slopes - 3 $.each(Scenario.rSlopes, function(idx, slope) { var cell = slope.cell, direction = slope.direction, adjacent = cell.getAdjacentCells(), mid = cell.getHexMidpoint("original"), angle = Math.PI - direction * Math.PI / 3, angle2 = angle - 2 * Math.PI / 3, angle3 = angle2 - Math.PI / 2, sin3 = Math.sin(angle3), cos3 = Math.cos(angle3); game.slopes.add(cell, adjacent[direction], 1); Math.seedrandom(cell.asCode()); var x = mid.x + game.HEX_SIDE * Math.cos(angle), y = mid.y - game.HEX_SIDE * Math.sin(angle), x2 = x + game.HEX_SIDE * Math.cos(angle2), y2 = y - game.HEX_SIDE * Math.sin(angle2), slp = 'M' + Math.round(x,2) + ' ' + Math.round(y,2) + 'L' + Math.round(x2,2) + ' ' + Math.round(y2,2); var start = game.hasSlope(cell, (direction + 1) % 6) ? 3 : -1, end = game.hasSlope(cell, (direction + 5) % 6) ? 8 : 12; for (var i = start; i < end; i++) { var len = Math.random() * game.HEX_SIDE / 3; if (i % 2) len += game.HEX_SIDE / 3; slp += ' ' + Math.round(x2 + (x-x2) * i / 10 + len * cos3,2) + ' ' + Math.round(y2 + (y-y2) * i / 10 - len * sin3,2); } game.paper.path(slp + 'z').attr( { stroke: "#844", fill: "#844", "stroke-width": 1, "stroke-linejoin": "round" }); });
  • 28. And so forth... Roads are like rivers Lakes and swamps are like forests
  • 29. My goal
  • 30. The result
  • 31. Making the Pieces Pieces are cardboard chits with graphics and text Can stack (and split and join) Can face different directions Can have status markers on top During the game they can Move Fire missiles Attack Be Eliminated
  • 32. Drawing the Icons
  • 33. Drawing the Icons - 2 Graphics Level 2 Stage one – try to write SVG Stage two – terror Stage three – check Dmitry's icons Stage four – terror Stage five – find an SVG drawing tool Light bulb moment – LibreOffice can export SVG Stage six – have a go
  • 34. Axe and Sword
  • 35. Axe and Sword - 2 <?xml version="1.0" encoding="UTF-8"?> <svg version="1.2" baseProfile="tiny" width="210mm" height="297mm" viewBox="0 0 21000 29700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <defs> <font id="EmbeddedFont_1" horiz-adv-x="2048"> <font-face font-family="Arial embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="450"/> <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/> <glyph unicode="I" horiz-adv-x="186" d="M 191,0 L 191,1466 385,1466 385,0 191,0 Z"/> <glyph unicode="B" horiz-adv-x="1086" d="M 150,0 L 150,1466 700,1466 C 812,1466 902,1451 970,1422 1037,1392 1090,1346 1129,1285 1167,1223 1186,1158 1186,1091 1186,1028 1169,969 1135,914 1101,859 1050,814 981,780 1070,754 1138,710 1186,647 1233,584 1257,510 1257,425 1257,356 1243,293 1214,234 1185,175 1149,129 1106,97 1063,65 1010,41 946,25 881,8 802,0 709,0 L 150,0 Z M 344,850 L 661,850 C 747,850 809,856 846,867 895,882 933,906 958,940 983,974 995,1017 995,1068 995,1117 983,1160 960,1197 937,1234 903,1259 860,1273 817,1286 742,1293 637,1293 L 344,1293 344,850 Z M 344,173 L 709,173 C 772,173 816,175 841,180 886,188 923,201 953,220 983,239 1008,266 1027,302 1046,337 1056,378 1056,425 1056,480 1042,527 1014,568 986,608 947,636 898,653 848,669 776,677 683,677 L 344,677 344,173 Z"/> </font> </defs> <g visibility="visible" id="MasterSlide_1_Default"> <desc>Master slide </desc> <rect fill="none" stroke="none" x="0" y="0" width="21000" height="29700"/> </g> <g visibility="visible" id="Slide_1_page1"> <g> <path fill="rgb(207,231,245)" stroke="none" d="M 1700,14900 L 2100,15400 7400,11500 6900,10900 1700,14900 Z"/> <path fill="none" stroke="rgb(128,128,128)" id="Drawing_1_0" stroke-linejoin="round" d="M 1700,14900 L 2100,15400 7400,11500 6900,10900 1700,14900 Z"/> </g> <g> <path fill="rgb(207,231,245)" stroke="none" d="M 8807,9464 L 9500,10100 11000,8700 11800,11300 14800,8500 11900,7700 12300,7400 11800,6800 11500,7100 11000,4300 7800,7000 10400,8200 8807,9464 Z"/> <path fill="none" stroke="rgb(128,128,128)" id="Drawing_2_0" stroke-linejoin="round" d="M 8807,9464 L 9500,10100 11000,8700 11800,11300 14800,8500 11900,7700 12300,7400 11800,6800 11500,7100 11000,4300 7800,7000 10400,8200 8807,9464 Z"/> </g> …... </g> </svg>
  • 36. Drawing the Icons - 3 $.UNITS = { // rawIcon comes from Open Office exported as SVG BI: { name: 'Barbarian Infantry', strength: 6, type: 'B', movement: 5, defHalf: true, rawIcon: ["M 1700,14900 L 2100,15400 7400,11500 6900,10900 1700,14900 Z", "M 8807,9464 L 9500,10100 11000,8700 11800,11300 14800,8500 11900,7700 12300,7400 11800,6800 11500,7100 11000,4300 7800,7000 10400,8200 8807,9464 Z", "M 12700,16000 L 13800,15000 12000,13900 12400,12700 2400,4500 12100,12700 11300,13700 2400,4500 10200,15000 11300,14400 12700,16000 Z"] }, BW: { name: 'Bowman', strength: 0, type: 'Ff', movement: 5, firepower: 2, range: 2, rawIcon: ["M 3000,9000 C 3000,9000 5000,4700 11200,4700 17300,4700 18900,9100 18900,9100 18900,9100 17100,5700 11200,5200 4700,5800 3000,9000 3000,9000 Z", "M 3000,9000 L 11000,13000 18900,9100 11000,13000", "M 11000,13000 L 11000,4000 10300,4000 11100,3100 12000,4000 11200,4000 11200,13000 11000,13000 Z"], }
  • 37. Drawing the Icons - 4 var widgets = [], unit = $.UNITS[code]; widgets.push($.UNITS.iconPath(code, width/2, x, y – width/8) .attr({fill: '#666', stroke: textColour})); widgets.push(game.paper.text(x, y + width / 4, (unit.defHalf ? '[' + unit.strength + ']' : (unit.strength ? '' + unit.strength : '*')) + ' ' + unit.type + ' ' + unit.movement) .attr({color: textColour, 'font-family': 'Times', 'font-size': '' + (width / 3.4) + 'px'})); widgets.push(game.paper.text(x - width * 7 / 16, y - width * 3 / 8, code) .attr({color: textColour, 'font-family': 'Times', 'font-size': '' + (width / 5) + 'px', 'text-anchor': 'start'})); if (unit.range != undefined) widgets.push( game.paper.text(x + game.UNIT_SIDE * 3 / 8, y, '' + unit.range) .attr({color: textColour, 'font-family': 'Times', 'font-size': '' + (width / 5) + 'px'})); var prefix = unit.leadership == undefined ? unit.firepower : unit.leadership; if (prefix != undefined) widgets.push( game.paper.text(x - width * 3 / 8, y, '' + prefix) .attr({color: textColour, 'font-family': 'Times', 'font-size': '' + (width / 5) + 'px'}));
  • 38. Putting it together
  • 39. With the original game... Stack the card board pieces Move them, counting terrain and different speeds Declare attacks Roll the die and apply the results Use the special behaviour of some pieces All the while, your opponent is watching you like a hawk to ensure you don't cheat The user interface must manage this now Hence we need controls
  • 40. Making controls Graphics Level 3 Deployment Control Movement Control Split Stack Control Waiting Control Combat Control Game Menu
  • 41. Deployment Control Placing your units at the start of the game Drag and drop off an “artist palette” Set facing Detect stacking violations
  • 42. Deployment Control - 2
  • 43. Deployment Control - 3 Raphaël has a method that manages drag and drop Can be applied to a set of elements Any element in the set is a handle Every element in the set moves I do lots of small movements as not only do the units drag, so does the whole palette Also, note the data() method below
  • 44. Deployment Control - 4 widget .data('code', code) .data('idx', idx) .drag(function (dx, dy, x, y, evt) { // Move if (control.startX == -1) return; control.chits[parseInt(this.data('idx'))] .transform(Raphael.format('...t{0} {1}', dx - control.cur.dx, dy - control.cur.dy)); control.cur = {dx:dx, dy:dy, x: x, y:y}; }, function(x, y, evt) { // Start if (control.limits[this.data('idx')] <= 0) { control.startX = -1; } control.cur = {dx: 0, dy:0, x: x, y: y}; }, function(evt) { // End if (control.startX == -1) return; var cell = game.getCellAt(control.cur.x, control.cur.y), stacks = game.getStack(cell.asCode()), code = this.data('code'), idx = parseInt(this.data('idx')); control.chits[idx].transform('...t ' + -control.cur.dx + ' ' + -control.cur.dy);
  • 45. Deployment Control - 5 Facing arrows are a bit of Raphaël elegance Draw the six arrows pointing up Rotate them around the “F”
  • 46. Deployment Control - 6 $.each(FACING, function(direction, label) { var x = 260, arrow = game.paper.path('M' + (x - 4) + ' 100v-10h-6l10 -13 9 13h-6v10z') .attr({stroke: '#090', fill: '#0a0', cursor: 'pointer'}); control.arrows.push(arrow); arrow.mouseover(function (evt) { arrow.attr({fill: '#dfd'}) }).mouseout(function (evt) { arrow.attr({fill: '#0a0'}) }).click(function (evt) { var stack = game.stacks[game.currentStack]; stack.face(direction, 'free'); stack.draw(); }).transform(Raphael.format('R {0} {1},120', game.toAngle(direction), x)); });
  • 47. Movement Control Each stack can move into its facing hexes Each hex costs a number of points to enter Only if it has the movement points Can always move one hex Changing facing costs one point If you make a mistake, you can reset and try again
  • 48. Movement Control - 2
  • 49. Movement Control - 3 Arrows are rotated same as deployment control They are hidden if not permitted Everything has to spring back if the reset is pressed So watch relative movement
  • 50. Movement Control - 4 this.stackDescription.attr({text: this.stack.getSummary()}); this.movesRemaining.attr({text: '' + this.stack.movementPlan.remaining + ' MPs'}); var control = this, stack = this.stack, adjacentCells = stack.cell.getAdjacentCells(), canMoveOne = (stack.disrupted == false) && (stack.movementPlan.remaining == stack.getStrength().movement); if (canMoveOne && control.stack.units.length > 1) { this.splitButton.show(); } else { this.splitButton.hide(); } $.each(this.faceArrows, function(direction, arrow) { if (stack.disrupted || (direction == stack.direction) || (stack.movementPlan.remaining <= 0)) { arrow.hide(); } else { arrow.show(); } }); $.each(this.moveArrows, function(direction, arrow) { if ((direction != stack.direction) && ((direction + 1) % 6 != stack.direction) && ((direction + 5) % 6 != stack.direction)) { arrow.hide(); } else { var moveCost = control.stack.movementCostFor(adjacentCells[direction]); if ((moveCost > control.stack.movementPlan.remaining) && !(canMoveOne && moveCost < 50)) { arrow.hide(); } else { arrow.show(); } } });
  • 51. Split Stack Control Units in a stack can split into two stacks Originally drag and drop, but as the behaviour was binary, I changed it to a simple click
  • 52. Split Stack Control - 2
  • 53. Split Stack Control - 3 for (var i = 0; i < this.leftUnits.length; i++) { var unit = control.stack.units[i], x = 20 + (i % 3) * gap, y = 60 + Math.floor( i / 3) * gap, chit = game.paper.rect(x, y, control.UNIT_SIDE, control.UNIT_SIDE).attr({fill: control.stack.side.colour, stroke: '#666', cursor: 'pointer'}); icon = $.UNITS.draw(unit, control.UNIT_SIDE, x + control.UNIT_SIDE/2, y + control.UNIT_SIDE/2, stack.side.textColour); icon.push(chit); control.chits.push(icon); $.each(icon, function(idx, widget) { widget .data('idx', i) .click(function (evt) { control.toggle(this.data('idx')); }); }); control.isLeft.push(true); } this.OKButton = new $.ControlButton(120, 178, 'OK', function () { control.save(); });
  • 54. Split Stack Control - 4 var unit = this.stack.units[idx]; if (this.isLeft[idx]) { this.chits[idx].transform('...t 195 0'); this.rightUnits.push(unit); for (var i = 0; i < this.leftUnits.length; i++) { if (this.leftUnits[i] == unit) { this.leftUnits.splice(i,1); break; } } } else { this.chits[idx].transform('...t -195 0'); this.leftUnits.push(unit); for (var i = 0; i < this.rightUnits.length; i++) { if (this.rightUnits[i] == unit) { this.rightUnits.splice(i,1); break; } } } this.isLeft[idx] = !this.isLeft[idx]; if ((this.leftUnits.length == 0) || (this.rightUnits.length == 0)) { this.OKButton.hide(); } else { this.OKButton.show(); }
  • 55. Waiting Control Not really a control Feed back on how you opponent is faring with their move Bar moves across to indicate progress
  • 56. Waiting Control - 2
  • 57. Waiting Control - 3 update: function(progress, message) { if (!this.showing) return; this.progress = progress; this.bar.remove(); this.bar = game.paper.rect(12 + this.origin.x,38 + this.origin.y, (this.width - 20) * progress, 12) .attr('fill', '#f00'); if (!this.show) this.bar.hide(); this.widgets.push(this.bar); if (message) this.status.attr('text', message); return this; },
  • 58. Combat Control No grey boxed needed When you highlight an assailant, I put a translucent target over potential victims When you click a target, I draw a translucent arrow from the assailant to the victim (Odds are shown on the right)
  • 59. Combat Control - 2
  • 60. Combat Control - 3 var midS = source.getHexMidpoint(), midT = target.getHexMidpoint(), range = Math.pow(Math.pow(midT.x - midS.x, 2) + Math.pow(midT.y - midS.y, 2), .5), slope = {x: (midT.x - midS.x) / range, y: (midT.y - midS.y) / range}; return game.paper.path( Raphael.format('M {0} {1}L{2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12}z', midS.x, midS.y, midT.x - (slope.x + slope.y / 3) * game.HEX_SIDE, midT.y - (slope.y - slope.x / 3) * game.HEX_SIDE, midT.x - (slope.x + 2 * slope.y / 3) * game.HEX_SIDE, midT.y - (slope.y - 2 * slope.x / 3) * game.HEX_SIDE, midT.x, midT.y, midT.x - (slope.x - 2 * slope.y / 3) * game.HEX_SIDE, midT.y - (slope.y + 2 * slope.x / 3) * game.HEX_SIDE, midT.x - (slope.x - slope.y / 3) * game.HEX_SIDE, midT.y - (slope.y + slope.x / 3) * game.HEX_SIDE)) .attr({fill: '#900', opacity: .5}) .toFront(); }
  • 61. Game Menu Zoom the board Rotate the board Resign Leave Replay Credit – All icons are Dmitry's
  • 62. Game Menu - 2
  • 63. Game Menu - 3 addControl: function(label, path, click) { var x = 10 + (this.nextLocation % 4) * 40, y = 48 + Math.floor(this.nextLocation / 4) * 40, elements = []; this.nextLocation ++; elements.push(game.paper.rect(x, y, 36, 36, 2) .attr({fill: '#00f', stroke: 'none'})); elements.push(game.paper.path(path) .attr({fill: '#ff0', stroke: 'none'}) .transform(Raphael.format('T{0} {1}', x + 2, y + 2))); elements.push(game.paper.text(this.width / 2, 36, label) .attr({fill: '#090', stroke: 'none'}).hide()); $.each(elements, function(idx, element) { element.mouseover(function () { elements[0].attr({fill: '#77f'}); elements[1].attr({fill: '#990'}); elements[2].show(); }).mouseout(function() { elements[0].attr({fill: '#00f'}); elements[1].attr({fill: '#ff0'}); elements[2].hide(); }).click(click); }); }
  • 64. Game Menu - 3 addControl: function(label, path, click) { var x = 10 + (this.nextLocation % 4) * 40, y = 48 + Math.floor(this.nextLocation / 4) * 40, elements = []; this.nextLocation ++; elements.push(game.paper.rect(x, y, 36, 36, 2) .attr({fill: '#00f', stroke: 'none'})); elements.push(game.paper.path(path) .attr({fill: '#ff0', stroke: 'none'}) .transform(Raphael.format('T{0} {1}', x + 2, y + 2))); elements.push(game.paper.text(this.width / 2, 36, label) .attr({fill: '#090', stroke: 'none'}).hide()); $.each(elements, function(idx, element) { element.mouseover(function () { elements[0].attr({fill: '#77f'}); elements[1].attr({fill: '#990'}); elements[2].show(); }).mouseout(function() { elements[0].attr({fill: '#00f'}); elements[1].attr({fill: '#ff0'}); elements[2].hide(); }).click(click); }); }
  • 65. Demo
  • 66. Where to next Touch screens and tablets More bug fixing and improvements What can be released? Post onto Source Forge or Git Hub Build a community
  • 67. Resources Code and documentation – http://raphaeljs.com Designer - http://dmitry.baranovskiy.com/ Google group https://groups.google.com/forum/#!forum/raphaeljs jQuery – http://jquery.com Moi – maurice@redwaratah.com
  • 68. Creating Masterpieces with Raphaël A journey with a JavaScript library Maurice Maneschi http://redwaratah.com