SlideShare une entreprise Scribd logo
1  sur  68
Creating Masterpieces
with Raphaël
A journey with a JavaScript library

Maurice Maneschi

http://redwaratah.com
Outline
Introducing Raphaël
Making a game board
Making the pieces
Making the controls
Demo
Where to next
Resources
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”
Goal
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
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
W
ARNING
THIS PRESENTATION MAY CONTAIN
TRACES OF TRIGONOMETRY.
VIEW DISCRETION IS ADVISED
ER
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
Samples – on the Raphaël site
Graphics have layers
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)
Making a Game Board
Layer 1
Hexagons to regular movement
Different terrain that impacts movement and combat
Different for each game
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'});
Drawing Lots of Hexagons

1
2
3
4
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);
}
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!
“We can always
count on the
Americans to do
the right thing,
after they have
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);
});
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
Drawing Forests - 2

A Failed Attempt
Drawing Forests - 3

3

1

2

4
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;
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"
});
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!
Drawing Slopes
Want a fan effect
Longer if end slope, shorter if next to another slope
Not too regular
A lot of experimentation
Drawing Slopes - 2

Why longer and shorter
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"
});
});
And so forth...
Roads are like rivers
Lakes and swamps are like forests
My goal
The result
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
Drawing the Icons
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
Axe and Sword
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>
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"],
}
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'}));
Putting it together
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
Making controls
Graphics Level 3
Deployment Control
Movement Control
Split Stack Control
Waiting Control
Combat Control
Game Menu
Deployment Control
Placing your units at the start of the game
Drag and drop off an “artist palette”
Set facing
Detect stacking violations
Deployment Control - 2
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
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);
Deployment Control - 5
Facing arrows are a bit of Raphaël elegance
Draw the six arrows pointing up
Rotate them around the “F”
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));
});
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
Movement Control - 2
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
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();
}
}
});
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
Split Stack Control - 2
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();
});
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();
}
Waiting Control
Not really a control
Feed back on how you opponent is faring with their
move
Bar moves across to indicate progress
Waiting Control - 2
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;
},
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)
Combat Control - 2
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();
}
Game Menu
Zoom the board
Rotate the board
Resign
Leave
Replay
Credit – All icons are Dmitry's
Game Menu - 2
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);
});
}
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);
});
}
Demo
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
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
Creating Masterpieces
with Raphaël
A journey with a JavaScript library

Maurice Maneschi

http://redwaratah.com

Contenu connexe

Tendances

The Ring programming language version 1.7 book - Part 57 of 196
The Ring programming language version 1.7 book - Part 57 of 196The Ring programming language version 1.7 book - Part 57 of 196
The Ring programming language version 1.7 book - Part 57 of 196Mahmoud Samir Fayed
 
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015pixelass
 
ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...
ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...
ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...Cyber Security Alliance
 
Intro to Game Programming
Intro to Game ProgrammingIntro to Game Programming
Intro to Game ProgrammingRichard Jones
 
Torturing the PHP interpreter
Torturing the PHP interpreterTorturing the PHP interpreter
Torturing the PHP interpreterLogicaltrust pl
 
Mobile Game and Application with J2ME - Collision Detection
Mobile Gameand Application withJ2ME  - Collision DetectionMobile Gameand Application withJ2ME  - Collision Detection
Mobile Game and Application with J2ME - Collision DetectionJenchoke Tachagomain
 
Mobile Game and Application with J2ME
Mobile Gameand Application with J2MEMobile Gameand Application with J2ME
Mobile Game and Application with J2MEJenchoke Tachagomain
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Suyeol Jeon
 
[3] 프로세싱과 아두이노
[3] 프로세싱과 아두이노[3] 프로세싱과 아두이노
[3] 프로세싱과 아두이노Chiwon Song
 
PyTrening 2.0 # 15 Okienka GUI
PyTrening 2.0 # 15 Okienka GUIPyTrening 2.0 # 15 Okienka GUI
PyTrening 2.0 # 15 Okienka GUIMoniaJ
 
Introduction to programming class 13
Introduction to programming   class 13Introduction to programming   class 13
Introduction to programming class 13Paul Brebner
 
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...tdc-globalcode
 
The Ring programming language version 1.2 book - Part 38 of 84
The Ring programming language version 1.2 book - Part 38 of 84The Ring programming language version 1.2 book - Part 38 of 84
The Ring programming language version 1.2 book - Part 38 of 84Mahmoud Samir Fayed
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is AwesomeAstrails
 
Cg my own programs
Cg my own programsCg my own programs
Cg my own programsAmit Kapoor
 
RxSwift 시작하기
RxSwift 시작하기RxSwift 시작하기
RxSwift 시작하기Suyeol Jeon
 
Computer Graphics Lab File C Programs
Computer Graphics Lab File C ProgramsComputer Graphics Lab File C Programs
Computer Graphics Lab File C ProgramsKandarp Tiwari
 
Taking Perl to Eleven with Higher-Order Functions
Taking Perl to Eleven with Higher-Order FunctionsTaking Perl to Eleven with Higher-Order Functions
Taking Perl to Eleven with Higher-Order FunctionsDavid Golden
 

Tendances (20)

The Ring programming language version 1.7 book - Part 57 of 196
The Ring programming language version 1.7 book - Part 57 of 196The Ring programming language version 1.7 book - Part 57 of 196
The Ring programming language version 1.7 book - Part 57 of 196
 
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
 
ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...
ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...
ASFWS 2012 - Obfuscator, ou comment durcir un code source ou un binaire contr...
 
Intro to Game Programming
Intro to Game ProgrammingIntro to Game Programming
Intro to Game Programming
 
Torturing the PHP interpreter
Torturing the PHP interpreterTorturing the PHP interpreter
Torturing the PHP interpreter
 
Mobile Game and Application with J2ME - Collision Detection
Mobile Gameand Application withJ2ME  - Collision DetectionMobile Gameand Application withJ2ME  - Collision Detection
Mobile Game and Application with J2ME - Collision Detection
 
Mobile Game and Application with J2ME
Mobile Gameand Application with J2MEMobile Gameand Application with J2ME
Mobile Game and Application with J2ME
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
 
Proga 0706
Proga 0706Proga 0706
Proga 0706
 
[3] 프로세싱과 아두이노
[3] 프로세싱과 아두이노[3] 프로세싱과 아두이노
[3] 프로세싱과 아두이노
 
PyTrening 2.0 # 15 Okienka GUI
PyTrening 2.0 # 15 Okienka GUIPyTrening 2.0 # 15 Okienka GUI
PyTrening 2.0 # 15 Okienka GUI
 
Introduction to programming class 13
Introduction to programming   class 13Introduction to programming   class 13
Introduction to programming class 13
 
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
 
The Ring programming language version 1.2 book - Part 38 of 84
The Ring programming language version 1.2 book - Part 38 of 84The Ring programming language version 1.2 book - Part 38 of 84
The Ring programming language version 1.2 book - Part 38 of 84
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is Awesome
 
Cg my own programs
Cg my own programsCg my own programs
Cg my own programs
 
Bow&amp;arrow game
Bow&amp;arrow gameBow&amp;arrow game
Bow&amp;arrow game
 
RxSwift 시작하기
RxSwift 시작하기RxSwift 시작하기
RxSwift 시작하기
 
Computer Graphics Lab File C Programs
Computer Graphics Lab File C ProgramsComputer Graphics Lab File C Programs
Computer Graphics Lab File C Programs
 
Taking Perl to Eleven with Higher-Order Functions
Taking Perl to Eleven with Higher-Order FunctionsTaking Perl to Eleven with Higher-Order Functions
Taking Perl to Eleven with Higher-Order Functions
 

En vedette

デザイナーがTkinterで遊んでみました。
デザイナーがTkinterで遊んでみました。デザイナーがTkinterで遊んでみました。
デザイナーがTkinterで遊んでみました。Chachamaru
 
Manual clips
Manual clipsManual clips
Manual clipsworkhome
 
Introdução a python módulo c
Introdução a python   módulo cIntrodução a python   módulo c
Introdução a python módulo cJader Gabriel
 
The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)
The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)
The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)Ranel Padon
 
Python Programming - III. Controlling the Flow
Python Programming - III. Controlling the FlowPython Programming - III. Controlling the Flow
Python Programming - III. Controlling the FlowRanel Padon
 
Python Programming - VII. Customizing Classes and Operator Overloading
Python Programming - VII. Customizing Classes and Operator OverloadingPython Programming - VII. Customizing Classes and Operator Overloading
Python Programming - VII. Customizing Classes and Operator OverloadingRanel Padon
 
Python Programming - IX. On Randomness
Python Programming - IX. On RandomnessPython Programming - IX. On Randomness
Python Programming - IX. On RandomnessRanel Padon
 
Python Programming - XI. String Manipulation and Regular Expressions
Python Programming - XI. String Manipulation and Regular ExpressionsPython Programming - XI. String Manipulation and Regular Expressions
Python Programming - XI. String Manipulation and Regular ExpressionsRanel Padon
 
Python Programming - VIII. Inheritance and Polymorphism
Python Programming - VIII. Inheritance and PolymorphismPython Programming - VIII. Inheritance and Polymorphism
Python Programming - VIII. Inheritance and PolymorphismRanel Padon
 
Batch Scripting with Drupal (Featuring the EntityFieldQuery API)
Batch Scripting with Drupal (Featuring the EntityFieldQuery API)Batch Scripting with Drupal (Featuring the EntityFieldQuery API)
Batch Scripting with Drupal (Featuring the EntityFieldQuery API)Ranel Padon
 
Power and Elegance - Leaflet + jQuery
Power and Elegance - Leaflet + jQueryPower and Elegance - Leaflet + jQuery
Power and Elegance - Leaflet + jQueryRanel Padon
 
Python Programming - VI. Classes and Objects
Python Programming - VI. Classes and ObjectsPython Programming - VI. Classes and Objects
Python Programming - VI. Classes and ObjectsRanel Padon
 
Python Programming - V. Sequences (List and Tuples) and Dictionaries
Python Programming - V. Sequences (List and Tuples) and DictionariesPython Programming - V. Sequences (List and Tuples) and Dictionaries
Python Programming - V. Sequences (List and Tuples) and DictionariesRanel Padon
 
Switchable Map APIs with Drupal
Switchable Map APIs with DrupalSwitchable Map APIs with Drupal
Switchable Map APIs with DrupalRanel Padon
 

En vedette (20)

20 cool things python
20 cool things python20 cool things python
20 cool things python
 
デザイナーがTkinterで遊んでみました。
デザイナーがTkinterで遊んでみました。デザイナーがTkinterで遊んでみました。
デザイナーがTkinterで遊んでみました。
 
Import python
Import pythonImport python
Import python
 
Manual clips
Manual clipsManual clips
Manual clips
 
Mba
MbaMba
Mba
 
Introdução a python módulo c
Introdução a python   módulo cIntrodução a python   módulo c
Introdução a python módulo c
 
The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)
The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)
The Synergy of Drupal Hooks/APIs (Custom Module Development with ChartJS)
 
Python IDE Roundup
Python IDE RoundupPython IDE Roundup
Python IDE Roundup
 
Python Programming - III. Controlling the Flow
Python Programming - III. Controlling the FlowPython Programming - III. Controlling the Flow
Python Programming - III. Controlling the Flow
 
Python Programming - VII. Customizing Classes and Operator Overloading
Python Programming - VII. Customizing Classes and Operator OverloadingPython Programming - VII. Customizing Classes and Operator Overloading
Python Programming - VII. Customizing Classes and Operator Overloading
 
Py S60
Py S60Py S60
Py S60
 
Python Programming - IX. On Randomness
Python Programming - IX. On RandomnessPython Programming - IX. On Randomness
Python Programming - IX. On Randomness
 
Python Programming - XI. String Manipulation and Regular Expressions
Python Programming - XI. String Manipulation and Regular ExpressionsPython Programming - XI. String Manipulation and Regular Expressions
Python Programming - XI. String Manipulation and Regular Expressions
 
Python Programming - VIII. Inheritance and Polymorphism
Python Programming - VIII. Inheritance and PolymorphismPython Programming - VIII. Inheritance and Polymorphism
Python Programming - VIII. Inheritance and Polymorphism
 
Batch Scripting with Drupal (Featuring the EntityFieldQuery API)
Batch Scripting with Drupal (Featuring the EntityFieldQuery API)Batch Scripting with Drupal (Featuring the EntityFieldQuery API)
Batch Scripting with Drupal (Featuring the EntityFieldQuery API)
 
Introducción a dr racket
Introducción a dr racketIntroducción a dr racket
Introducción a dr racket
 
Power and Elegance - Leaflet + jQuery
Power and Elegance - Leaflet + jQueryPower and Elegance - Leaflet + jQuery
Power and Elegance - Leaflet + jQuery
 
Python Programming - VI. Classes and Objects
Python Programming - VI. Classes and ObjectsPython Programming - VI. Classes and Objects
Python Programming - VI. Classes and Objects
 
Python Programming - V. Sequences (List and Tuples) and Dictionaries
Python Programming - V. Sequences (List and Tuples) and DictionariesPython Programming - V. Sequences (List and Tuples) and Dictionaries
Python Programming - V. Sequences (List and Tuples) and Dictionaries
 
Switchable Map APIs with Drupal
Switchable Map APIs with DrupalSwitchable Map APIs with Drupal
Switchable Map APIs with Drupal
 

Similaire à Creating masterpieces with raphael

need help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdfneed help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdfarcotstarsports
 
Артём Акуляков - F# for Data Analysis
Артём Акуляков - F# for Data AnalysisАртём Акуляков - F# for Data Analysis
Артём Акуляков - F# for Data AnalysisSpbDotNet Community
 
Computer graphics lab manual
Computer graphics lab manualComputer graphics lab manual
Computer graphics lab manualUma mohan
 
SaveI need help with this maze gui that I wrote in java, I am tryi.pdf
SaveI need help with this maze gui that I wrote in java, I am tryi.pdfSaveI need help with this maze gui that I wrote in java, I am tryi.pdf
SaveI need help with this maze gui that I wrote in java, I am tryi.pdfarihantstoneart
 
Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...
Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...
Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...Mozaic Works
 
2Bytesprog2 course_2014_c9_graph
2Bytesprog2 course_2014_c9_graph2Bytesprog2 course_2014_c9_graph
2Bytesprog2 course_2014_c9_graphkinan keshkeh
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeLaurence Svekis ✔
 
Advanced Data Visualization Examples with R-Part II
Advanced Data Visualization Examples with R-Part IIAdvanced Data Visualization Examples with R-Part II
Advanced Data Visualization Examples with R-Part IIDr. Volkan OBAN
 
ECMAScript 6 major changes
ECMAScript 6 major changesECMAScript 6 major changes
ECMAScript 6 major changeshayato
 
How to make a video game
How to make a video gameHow to make a video game
How to make a video gamedandylion13
 
The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)GroovyPuzzlers
 
A quick introduction to R
A quick introduction to RA quick introduction to R
A quick introduction to RAngshuman Saha
 
Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語ikdysfm
 

Similaire à Creating masterpieces with raphael (20)

need help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdfneed help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdf
 
Артём Акуляков - F# for Data Analysis
Артём Акуляков - F# for Data AnalysisАртём Акуляков - F# for Data Analysis
Артём Акуляков - F# for Data Analysis
 
CoW Documentatie
CoW DocumentatieCoW Documentatie
CoW Documentatie
 
Computer graphics lab manual
Computer graphics lab manualComputer graphics lab manual
Computer graphics lab manual
 
SaveI need help with this maze gui that I wrote in java, I am tryi.pdf
SaveI need help with this maze gui that I wrote in java, I am tryi.pdfSaveI need help with this maze gui that I wrote in java, I am tryi.pdf
SaveI need help with this maze gui that I wrote in java, I am tryi.pdf
 
Canvas
CanvasCanvas
Canvas
 
Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...
Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...
Stefan Kanev: Clojure, ClojureScript and Why They're Awesome at I T.A.K.E. Un...
 
2Bytesprog2 course_2014_c9_graph
2Bytesprog2 course_2014_c9_graph2Bytesprog2 course_2014_c9_graph
2Bytesprog2 course_2014_c9_graph
 
Internal workshop es6_2015
Internal workshop es6_2015Internal workshop es6_2015
Internal workshop es6_2015
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
 
C programs
C programsC programs
C programs
 
Advanced Data Visualization Examples with R-Part II
Advanced Data Visualization Examples with R-Part IIAdvanced Data Visualization Examples with R-Part II
Advanced Data Visualization Examples with R-Part II
 
ECMAScript 6 major changes
ECMAScript 6 major changesECMAScript 6 major changes
ECMAScript 6 major changes
 
Game dev 101 part 3
Game dev 101 part 3Game dev 101 part 3
Game dev 101 part 3
 
How to make a video game
How to make a video gameHow to make a video game
How to make a video game
 
The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)
 
A quick introduction to R
A quick introduction to RA quick introduction to R
A quick introduction to R
 
10 linescan
10 linescan10 linescan
10 linescan
 
Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
 

Dernier

EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdfChristopherTHyatt
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfhans926745
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 

Dernier (20)

EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 

Creating masterpieces with raphael

  • 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”
  • 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
  • 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
  • 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
  • 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
  • 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'}));
  • 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
  • 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
  • 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
  • 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
  • 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)
  • 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
  • 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