In a previous post Playing around with JavaScript and 2D Graphics I was practicing JavaScript to implement a small AutoCAD-like 2D web application.
The main limitation when coding in JavaScript is that, as a prototypal language - I knew I could place the word somewhere! – it doesn’t support classes and inheritance out of the box, which seems to be for some people an interesting feature but sounds to me rather like a straight pain …
Google’s answer to that is called Dart, which they define as a new platform
for scalable web app engineering. See the Dart website at: https://www.dartlang.org
I decided to give it a try and migrate my JavaScript app from last time to see how easier – or harder – was to write a Dart app comparing to JavaScript. First of all Dart is coming with a dedicated IDE, obviously free and similar to a stripped down version of Eclipse, which is pretty cool and very straightforward to get used to.
Dart is a class based object oriented language, so let see how a Dart class may look like. I decided to start with the pinnacle of computer mathematics by creating the mighty Poin2d class:
// Name of our lib
library JsCadMath;
// external imports
// imports can be renamed to avoid name collision
import 'dart:math' as math;
///////////////////////////////////////////////////////////////////////////////
// Represents a 2d Point
//
///////////////////////////////////////////////////////////////////////////////
class Point2d
{
double x;
double y;
// Constructor
Point2d(this.x, this.y);
// Equivalent to
//
// Point2d(num X, num Y)
// {
// this.x = X;
// this.y = Y;
// }
// Defines a constant instance of Point2d class
Point2d.zero():
x = 0.0,
y = 0.0;
// Method that converts a Point2d to a String
String ToString()
{
return x.toString() + ', ' + y.toString();
}
// Another method that adds points
Point2d Add(Point2d p)
{
return new Point2d(
x + p.x,
y + p.y);
}
// Returns distance between points
double Dist(Point2d p)
{
return math.sqrt(
(x - p.x) * (x - p.x) +
(y - p.y) * (y - p.y));
}
}
-
The Dart syntax is a mix of JavaScript / Java / C# and supports dynamic types as well typed parameters. It will feel more or less intuitive to any programmer familiar with one or several of those languages.
Dart is coming up with a bunch of existing libraries which I didn’t really investigate in depth, however those are nothing compared to the JavaScript frameworks we can find out there. So my next concern was obviously Can we invoke JavaScript libraries from dart code? And the answer is yes! JavaScript code cannot be invoked directly in Dart, however there is an interop feature that allows to instantiate JavaScript proxies and call their methods.
Since my app was based on the 2D framework Raphaël, I investigated how I could use it from Dart. All you need to do is creating a wrapper around the needed JavaScript objects and their methods, pretty much the same way you would create a .Net wrapper for unmanaged C++ classes.
Here is the implementation of my Dart Raphaël wrapper:
///////////////////////////////////////////////////////////////////////////////
// Dart wrapper for Raphael JavaScript
//
///////////////////////////////////////////////////////////////////////////////
class Raphael
{
// JavaScript Raphael object
js.JsObject _raphaelJs;
// Constructor
Raphael(String containerId, int width, int height)
{
var container = querySelector(containerId);
// Instanciate JavaScript object with arguments
_raphaelJs = new js.JsObject(
js.context['Raphael'],
[container, width, height]);
}
// Wrapper for Raphael.circle()
GraphicElement Circle(Point2d center, double radius)
{
js.JsObject element = _raphaelJs.callMethod(
'circle',
[center.x, center.y, radius]);
return new GraphicElement(element);
}
// Wrapper for Raphael.setStart()
void SetStart()
{
_raphaelJs.callMethod(
'setStart');
}
// Wrapper for Raphael.setFinish()
GraphicElement SetFinish()
{
js.JsObject element = _raphaelJs.callMethod(
'setFinish');
return new GraphicElement(element);
}
// Wrapper for Raphael.path()
GraphicElement Path(String path)
{
js.JsObject element = _raphaelJs.callMethod(
'path',
[path]);
return new GraphicElement(element);
}
}
///////////////////////////////////////////////////////////////////////////////
// Dart wrapper for Raphael.Element
//
///////////////////////////////////////////////////////////////////////////////
class GraphicElement
{
// JavaScript Raphael.Element object
js.JsObject _element;
GraphicElement(js.JsObject element)
{
_element = element;
}
Object GetAttribute(String name)
{
return _element.callMethod('attr', [name]);
}
void SetAttribute(String name, value)
{
_element.callMethod('attr', [name, value]);
}
// So this is pretty cool:
// In the newer version of Dart a Dart method can
// be provided directly as callback argument to a
// JavaScript invoked method.
// The Dart interop will handle automatically the
// marshalling, so the programmer doesn't have to worry
// about anything :)
-
void SetDraggable(
onDragMove,
onDragStart,
onDragStop)
{
_element.callMethod(
'drag',
[onDragMove,
onDragStart,
onDragStop]);
}
void SetClickable(
onClick)
{
_element.callMethod(
'click',
[onClick]);
}
void SetPosition(Point2d position)
{
SetAttribute('cx', position.x);
SetAttribute('cy', position.y);
}
void Move(Point2d offset)
{
var cX = GetAttribute('cx');
var cY = GetAttribute('cy');
SetAttribute('cx', cX + offset.x);
SetAttribute('cy', cY + offset.y);
}
void Remove()
{
_element.callMethod('remove');
}
void ForEachSubElement(f)
{
for(int i=0; i<_element['length']; ++i)
{
f(_element[i]);
}
}
void ForSubElement(int index, f)
{
f(_element[index]);
}
}
I just had to carry on the implementation of my various Dart classes based on my previous JavaScript code and my Raphael wrapper. It results in a structured web app project that looks like below in the Dart IDE:
The html implementation is minimal:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JsCad - Dart Edition</title>
<link rel="stylesheet" href="jscad.css">
</head>
<body>
<h1>Welcome to ADN Dart Demo!</h1>
<div id="canvas_container"></div>
<script type="application/dart" src="jscad.dart"></script>
<script src="packages/js/raphael.js"></script>
</body>
</html>
As well as the Dart App itself:
// imports for our dart files
import '../lib/Raphael.dart';
import '../lib/JsCadMath.dart';
import '../lib/JsCadLine.dart';
import '../lib/JsCadCircle.dart';
import '../lib/JsCadPLine.dart';
import '../lib/JsCadSpline.dart';
import '../lib/JsCadApplication.dart';
// Global JsCadApplication variable
JsCadApplication JsCadApp = null;
///////////////////////////////////////////////////////////////////////////////
// Dart Entry point
//
///////////////////////////////////////////////////////////////////////////////
void main()
{
// creates Raphael layout
// using div container defined in html
var paper = new Raphael(
"#canvas_container",
700,
700);
// creates JsCadApplication
JsCadApp = new JsCadApplication();
// new database based on Raphael layout
var db = JsCadApp.NewDatabase(paper);
// new line entity
var line = new JsCadLine(
new Point2d(10.0, 10.0),
new Point2d(250.0, 250.0));
// add to database
db.AddToDatabase(line);
// new circle entity
var circle = new JsCadCircle(
new Point2d(350.0, 150.0),
100);
db.AddToDatabase(circle);
// new pline entity
var pline = new JsCadPLine(
[new Point2d(10.0, 250.0),
new Point2d(50.0, 400.0),
new Point2d(150.0, 250.0),
new Point2d(200.0, 350.0),
new Point2d(350.0, 280.0)]);
db.AddToDatabase(pline);
// new spline entity
var spline = new JsCadSpline(
[new Point2d(10.0, 500.0),
new Point2d(50.0, 650.0),
new Point2d(150.0, 500.0),
new Point2d(210.0, 400.0),
new Point2d(250.0, 550.0),
new Point2d(280.0, 650.0),
new Point2d(450.0, 400.0)]);
db.AddToDatabase(spline);
}
-
Ok Dart seems cool, but what if I need JavaScript code to embed in my web page…?
No worries, Dart compiles into JavaScript:
You will find the complete project in the attachment below.
Comments