Creating a simple drawing area widget with google closure - javascript

Here I've tried to create a simple drawing area widget containing a single circle, using google closure.
I load it by calling sketcher.load() within html script tag and get an error:
Uncaught TypeError: Cannot set property 'Widget' of undefined - what is not right here?
goog.provide('sketcher');
goog.require('goog.dom');
goog.require('goog.graphics');
goog.require('goog.ui.Component');
var sketcher = {};
sketcher.prototype.Widget = function(el){
goog.ui.Component.call(this);
this.parent_ = goog.dom.getElement(el);
this.g_ = new goog.graphics.createGraphics(600, 400);
this.appendChild(this.g_);$
var fill = new goog.graphics.SolidFill('yellow');
var stroke = new goog.graphics.Stroke(1,'black');
circle = this.g_.drawCircle(300, 200, 50, stroke, fill);
this.g_.render(this._parent);
};
goog.inherits(sketcher.Widget, goog.ui.Component);
sketcher.prototype.load = function(){
var canvas = goog.dom.createDom('div', {'id':'canvas'});
goog.dom.appendChild(document.body, canvas);
var widget = new sketcher.Widget(canvas);
};

First problem: sketcher is a namespace, because you goog.provide it. You don't need to declare it again.
Second problem: sketcher.Widget should be thus, not sketcher.prototype.Widget. Only functions have prototypes; you should go back and review how objects work in JavaScript unless that was just a typo. It should look like this.
goog.provide('sketcher');
goog.require('goog.dom');
goog.require('goog.graphics');
goog.require('goog.ui.Component');
/**
* My sketcher widget.
* #param {Element} e1
* #constructor
*/
sketcher.Widget = function(el){
goog.ui.Component.call(this);
this.parent_ = goog.dom.getElement(el);
this.g_ = new goog.graphics.createGraphics(600, 400);
this.appendChild(this.g_);$
var fill = new goog.graphics.SolidFill('yellow');
var stroke = new goog.graphics.Stroke(1,'black');
circle = this.g_.drawCircle(300, 200, 50, stroke, fill);
this.g_.render(this._parent);
};
goog.inherits(sketcher.Widget, goog.ui.Component);
sketcher.prototype.load = function(){
var canvas = goog.dom.createDom('div', {'id':'canvas'});
goog.dom.appendChild(document.body, canvas);
var widget = new sketcher.Widget(canvas);
};

here is a link to a simple svg drawing widget built with closure
http://webos-goodies.googlecode.com/svn/trunk/blog/articles/make_a_drawing_tool_with_closure_library/simple-draw.js

Related

How to override/ replace existing object

Due demand, I give all the code in respect to onmouseClick:
First I import svg each in a new layer into canvas:
//imports svg from external js script
var svgimportSVG = function (elem,x) {
// creates a new layer for each imported svg
var layer = new Layer();
layer.name = "Layer" +x;
layer.activate();
// import here
rect = paper.project.importSVG(elem,function(item){
console.log("Elem: " + elem);
});
Then I build the "frame" as a path on bounds:
// build a frame
rect.data.showBounds = new Path.Rectangle({
rectangle: rect.bounds,
strokeColor: 'aqua'
});
return rect;
}
I want the effect that only the clicked element or the least imported element has a visible frame:
tool.onMouseDown = function(e) {
var hitResult = project.hitTest(e.point, hitOptions);
if(hitResult){
var SVGGroup = searchSVGGroup(hitResult.item,"svg");
var SVGLayer = searchSVGGroup(hitResult.item,"layer");
// Layer of clicked object shall be in front or last element in project.layers
console.log(SVGLayer.name);
var IndexOfLayer = project.layers.indexOf(SVGLayer);
project.layers.splice(IndexOfLayer,1);
project.layers.push(SVGLayer);
var LayersLengthnew = project.layers.length;
//Clicked object can be influenced ouside paperscope
window.globals.extExportOnMouseClick(project.layers[LayersLengthnew -1].name);
SVGGroup.data.showBounds = new Path.Rectangle({
rectangle: rect.bounds,
strokeColor: 'aqua'
});
// sent as old clicked object
svgSetRemoveFrameofGroup(SVGGroup);
}
else{...}
}
//create an empty object that holds the last clicked object
var svgFramedGroup = {};
var svgSetRemoveFrameofGroup = function(rect){
var rectold = {};
console.log("rect name: "+ rect.name); // gives correct name
if(svgFramedGroup.data){
rectold = svgFramedGroup;
console.log("rect old name: "+ rectold.name); // gives correct name
}
svgFramedGroup = rect;
if(!svg_isEmpty(rectold)){
//... showBounds should go to Garbage collector
rectold = rectold.data.showBounds.remove();
}
return rectold;
}
But the further clicked element has still its bounds which is a path!?
I answered you here for the same question.
The only different thing is that here you're asking for a "frame" shown when an element is clicked and there you're asking for it to be hidden when element is clicked (which is what I did in my example).
But you should be able to get the logic anyway.

paper.Tool is not working on Multiple canvas

I have two canvas the first one is working good, but when I initialize the second one the paper.Tool does not work properly, sometimes the event onMouseMove works others not.
var dataLoad;
var mypapers = []
$(document).ready(function () {
dataLoad = new DataLoad();
mypapers[0] = new paper.PaperScope();
mypapers[1] = new paper.PaperScope();
mypapers[0].setup(document.getElementById('dataCanvas'));
dataLoad.Init();
});
// "returnedData" THIS ARRAY COMES FROM AN AJAX CALL
DataLoad.prototype = {
Init: function () {
var self = this;
var paperData = new
DataReader(document.getElementById('dataCanvas'));
paperData.Init(returnedData[i],mypapers[0]);
paperData.Draw(true);
self.datas.push(paperData);
}
});
Till here everything is good the first canvas is populated with the graphics I setted.
DataReader.prototype = {
Init: function (data,mypaper) {
var self = this;
paper = mypaper;
self.paper = paper;
self.toolPan = new self.paper.Tool()
self.toolPan.activate();
self.toolPan.onMouseDrag = function (event) {
var delta = event.downPoint.subtract(event.point)
self.paper.view.scrollBy(delta)
};
self.toolPan.onMouseMove = function (event) {
self.OnMouseMove(event);
};
self.toolPan.onMouseUp = function (event) {
// AFTER MAKE A SELECTION OF ITEMS IN THE CANVAS CALLING THE SECOND CANVAS
var core = self.HighlightElementsInBox();
self.dc = new DefineComponent();
self.dc.Init(core);
$('#DCCanvas').modal('toggle'); // THE CANVAS IS INSIDE THIS BOOTSTRAP MODAL
}
}
});
/* this initialize the second canvas that basically creates another instance of the same prototype i use to manipulate paperjs in the first canvas */
DefineComponent.prototype = {
Init: function (dataCore) {
var self = this;
mypapers[1].setup(document.getElementById('DCCanvas')); // setting second canvas
var paperDataDC = new DataReader(document.getElementById('DCCanvas'));
paperDataDC.Init(dataCore,mypapers[1]);
paperDataDC.Draw(true);
self.datas.push(paperDatasDC);
}
});
In this second canvas all is drawn correctly, but the events onmousedrag and onmousemove does not works properly the first one move the canvas in another position where the mouse is not and mousemove works only in some places of the canvas not in all.
As you are creating two different paperScopes you will need to toggle between them when working with one or the other.
You are saving both paperScopes inside "mypapers" array
mypapers[0] = new paper.PaperScope();
mypapers[1] = new paper.PaperScope();
So to use any of those you should do
mypapers[0].activate();
// or
mypapers[1].activate();
Check out this (min setup) example of what I mean above.
Also, follow stefan's suggestion if you want more help on this since it's hard for people to try to help without a minimal working example

How to get the current pattern-object contents of a canvas-context (2D)?

(Using Firefox32, and Win7. But in other browsers I need it to work, too.)
I can't find a command to retrieve the content of the pattern-object that I set on the 2D-context.
Is there no direct way to get the value array and the width and height?
And if there is really no direct way, is there a workaround?
I could just use fillRect with the pattern on a hidden canvas and then reading out the canvas. But then, how to get the correct height and width?
Pattern properties
The only method exposed on the CanvasPattern object is to handle transformations:
interface CanvasPattern {
// opaque object
void setTransform(SVGMatrix transform);
};
This means all other properties has to be tracked externally.
Workaround 1 - manually keep track of properties
The workaround is to read the width and height from the image you used for the pattern, as well as mode and optionally transforms.
Just keep a reference to them for later:
var img = ...; // image source
var patternMode = "repeat"; // store repeat mode
var patternWidth = img.naturalWidth; // width and height of image
var patternHeight = img.naturalHeight; // = width and height of pattern
var pattern = ctx.createPattern(img, patternMode); // use to create pattern
Workaround 2 - create a custom object
You can create a custom object which wraps up the pattern creation process and exposes methods that can hold width, height etc.
Example
An object could look like this:
function PatternExt(ctx, image, mode) {
var ptn = ctx.createPattern(image, mode || "repeat");
this.setTransform = ptn.setTransform ? ptn.setTransform.bind(ptn) : null;
this.width = image.naturalWidth;
this.height = image.naturalHeight;
this.image = image;
this.mode = mode;
this.pattern = ptn;
}
Then it's just a matter of creating an instance almost the same way as with createPattern():
var p = new PatternExt(ctx, img, "repeat");
ctx.fillStyle = p.pattern;
To read information do:
var w = p.width;
var h = p.height;
...
Rename/extend as you want/need.
Demo for custom object
// load an image for pattern
var img = new Image();
img.onload = demo;
img.src = "http://i.imgur.com/HF5eJZS.gif";
function demo() {
var ctx = document.querySelector("canvas").getContext("2d"), p;
// create a pattern instance
p = new PatternExt(ctx, img, "repeat");
// use as fill-style
ctx.fillStyle = p.pattern;
ctx.fillRect(0, 0, 300, 150);
// show some properties
ctx.font = "24px sans-serif";
ctx.fillStyle = "#fff";
ctx.fillText([p.width, p.height, p.mode].join(), 10, 30);
}
function PatternExt(ctx, image, mode) {
var ptn = ctx.createPattern(image, mode || "repeat");
this.setTransform = ptn.setTransform ? ptn.setTransform.bind(ptn) : null;
this.width = image.naturalWidth;
this.height = image.naturalHeight;
this.image = image;
this.mode = mode;
this.pattern = ptn;
}
<canvas></canvas>
If your desired pattern is currently used as the fillStyle, then you can fetch it by fetching the fillStyle:
myPattern=context.fillStyle;
Otherwise you can't fetch your pattern object because the context keeps any pattern objects you've created as private properties.
So typically you keep a reference to your pattern until it's not needed anymore.
If you also need the original imageObject used to create your pattern then you typically save a reference to that image also.
// create an imageObject for use in your pattern
var myImageObject=new Image();
myImageObject.onload=start; // call start() when myImageObject is fully loaded
myImageObject.src="";
function start(){
// myImageObject has now been fully loaded so
// create your pattern and keep a reference to it
var myPattern = context.createPattern(myImageObject, 'repeat');
}
... and later when you need the pattern ...
// use your pattern object reference to apply the pattern as a fillStyle
context.fillStyle = myPattern;
... and later if you need the original image object
// get the original image object's size
var imgWidth=myImageObject.width;
var imgHeight=myImageObject.height;
// draw the original image object to the context -- or whatever you need it for
context.drawImage(myImageObject,50,50);

EaselJS: BitmapSequence issue in a OOP-like class

I have been playing a bit with gskinner.com's EaselJS library (http://easeljs.com/, http://easeljs.com/examples/game/game.html), which makes dealing with HTML5's canvas a lot easier.
So I'm trying to remake something like Space Invaders in the canvas. So far it's not much, just the payer moving left to right. See the progress here: http://jansensan.net/experiments/easeljs-space-invader/
For the invaders, I needed an animation, so I followed a tutorial on how to do so: http://www.youtube.com/watch?v=HaJ615V6qLk
Now that is all good and dandy, however I follow gskinner.com's way of creating "classes": http://easeljs.com/examples/game/Ship.js I'm not certain if I can call that a class, but it is used as such.
So below is the class that I wrote for the Invader, however it seems like the BitmapSequence does not seem to be added to EaselJS's stage. Anyone can guide me through this? Thanks!
// REFERENCES
/*
http://easeljs.com/examples/game/Ship.js
*/
// CLASS
(function(window)
{
function Invader()
{
this.initialize();
}
var p = Invader.prototype = new Container();
// CONSTANTS
// VARS
p.image;
p.bitmapSequence;
// CONSTRUCTOR
p.Container_initialize = p.initialize; //unique to avoid overiding base class
p.initialize = function()
{
this.Container_initialize();
this.image = new Image();
this.image.onload = p.imageLoadHandler;
this.image.onerror = p.imageErrorHandler;
this.image.src = "assets/images/invader-spritesheet.png";
}
p.imageLoadHandler = function()
{
var frameData = {
anim:[0, 1, "anim"]
}
var spriteSheet = new SpriteSheet(p.image, 22, 16, frameData);
p.bitmapSequence = new BitmapSequence(spriteSheet);
p.bitmapSequence.regX = p.bitmapSequence.spriteSheet.frameWidth * 0.5;
p.bitmapSequence.regY = p.bitmapSequence.spriteSheet.frameHeight * 0.5;
p.bitmapSequence.gotoAndStop("anim");
p.addChild(p.bitmapSequence);
}
p.imageErrorHandler = function()
{
console.log("Error: the url assets/images/invader-spritesheet.png could not be loaded.");
}
window.Invader = Invader;
}(window));
Does you p.image/this.Container_initalize actually exist at that point? As you switch between using this. and p. between your init and other functions, while they might seem to be the same practice has often taught me its not the case. Try changing your init function to this:
p.initialize = function()
{
p.Container_initialize();
p.image = new Image();
p.image.onload = p.imageLoadHandler;
p.image.onerror = p.imageErrorHandler;
p.image.src = "assets/images/invader-spritesheet.png";
}

gjs cairo context drawing not displaying in clutter window

I'm trying to get a simple cairo drawing drawn to a clutter window using the javascript bindings. My problem is, apart from half the functions being named slightly differently, is no matter what I try, the cairo drawing does not show up. I used a example from python, which does work, and ported it to javascript. I'm also using introspection to get the Clutter module instance. I'm also using gjs version 0.7.14. Can anyone tell me what is going wrong.
Below is the example code I'm using.
const cairo = imports.cairo;
const clutter = imports.gi.Clutter;
function on_button_press_event (stage, event) {
clutter.main_quit();
}
function draw(cairo_tex) {
var context = cairo_tex.create();
context.scale(200, 200);
context.setLineWidth(0.1);
var colour2 = new clutter.Color();
colour2.from_string('#dd000088');
clutter.cairo_set_source_color(context, colour2);
context.translate(0.5, 0.5);
context.arc(0, 0, 0.4, 0, Math.PI * 2);
context.stroke();
}
function main () {
clutter.init(0, null);
var stage = new clutter.Stage();
var colour = new clutter.Color();
colour.from_string("#ffccccff");
stage.set_color(colour);
stage.set_size(400, 300);
stage.connect('button-press-event', on_button_press_event);
stage.connect('destroy', clutter.main_quit);
var cairo_tex = new clutter.CairoTexture.new(200, 200);
cairo_tex.set_position((stage.get_width() - 200) / 2,
(stage.get_height() - 200) / 2);
draw(cairo_tex);
var center_x = cairo_tex.get_width() / 2;
var center_z = cairo_tex.get_height() / 2;
cairo_tex.set_rotation(clutter.AlignAxis.Y_AXIS, 45.0, center_x, 0, center_z);
stage.add_actor(cairo_tex);
cairo_tex.show();
stage.show();
clutter.main();
}
main();
I think the reason this is not working has to do with the deletion of the cairo context in javascript. context.destroy does not exist and using delete doesn't either. Infact if I use delete then I get the warning
WARNING: 'applying the 'delete' operator to an unqualified name is deprecated'
which does not help at all. According to what some of the developers involved in gjs have posted about it, assigning to null should have the same effect, due to it being garbage collected. I'm having my doubts as to whether there is anything to collect behind the scenes.
If someone could say if this is true or not, then I would accept this as a answer.
UPDATE:
I have narrowed down the problem area to imports.gi.Clutter. I tried another example, but this time using Gtk instead of Clutter, and the following code actually works
cairo = imports.cairo;
Gtk = imports.gi.Gtk;
Gdk = imports.gi.Gdk;
function draw_arc(drawing_area){
var cr = Gdk.cairo_create(drawing_area.get_window());
cr.scale(2, 2);
cr.operator = cairo.Operator.CLEAR;
cr.paint();
cr.operator = cairo.Operator.OVER;
cr.setSourceRGB(0,255,0);
cr.arc(128, 128, 76.8, 0, 2*Math.PI);
cr.fill();
return false;
}
Gtk.init(0, null);
var w = new Gtk.Window();
w.connect('delete-event', Gtk.main_quit);
var d = new Gtk.DrawingArea();
w.add(d);
w.resize(500,600);
w.decorated = false;
d.connect('draw', draw_arc);
w.show_all();
Gtk.main();
This leads me to believe that the problem is not with the gjs implementation of cairo, but with the gjs introspection methods for Clutter Cairo implementation. I'm thinking that clutter.CairoTexture.new or clutter.CairoTexture.create is not implemented properly. I suspect it is the clutter.CairoTexture.create which is causing the problem.
Using the newer integration between Clutter, and Cairo, specifically the Clutter.Canvas this will draw a circle to the screen:
const Clutter = imports.gi.Clutter;
const Cairo = imports.cairo;
const draw_stuff = function (canvas, cr, width, height) {
cr.save ();
cr.setOperator (Cairo.Operator.CLEAR);
cr.paint ();
cr.restore ();
cr.setOperator (Cairo.Operator.OVER);
cr.scale (width, height);
cr.setLineCap (Cairo.LineCap.ROUND);
cr.setLineWidth (0.1);
cr.translate (0.5, 0.5);
cr.arc (0, 0, 0.4, 0, Math.PI * 2);
cr.stroke ();
return true;
};
const test = function () {
Clutter.init(null);
let stage = new Clutter.Stage();
stage.set_title ("Circle!");
let color = new Clutter.Color({
red : 255,
green : 0,
blue : 0,
alpha : 128 // Just for the heck of it.
});
stage.set_background_color(color);
stage.set_size(300, 300);
let canvas = new Clutter.Canvas ();
canvas.set_size (155, 155);
let dummy = new Clutter.Actor ();
dummy.set_content (canvas);
dummy.set_size(155, 155);
stage.add_child (dummy);
stage.connect ("destroy", Clutter.main_quit);
canvas.connect ("draw", draw_stuff);
canvas.invalidate ();
stage.show_all();
Clutter.main ();
};
test ();

Categories