How to build Image Editor using HTML5 and Raphael JS? - javascript

I am building a web application and for this I am building a basic Image Editor using HTML5 and Raphael js. I have implemented some features like:
Resize Flip horizontal and/or vertical Drag
Some more features I need to implement are :
Crop Undo/Redo Changing background colour and opacity.
I am mainly looking for crop and undo functionality. Can anyone help me to achieve this using RaphaelJs and HTML5?
My Code:
var r, c, d;
var paper = Raphael("editor", 600,450);
var canvas = document.getElementById('editor');
canvas.style.backgroundColor='gray';
$scope.current;
var someFunction = function(e) {
e.data('dragged', false);
e.drag(dragmove, dragstart, dragend);
for(var i in elements) {
var thisShape = elements[i];
var ft = paper.freeTransform(thisShape,{draw:['bbox']});
ft.hideHandles();
thisShape.click(function(){
paper.forEach(function(el) {
if(el.freeTransform.handles.bbox != null)
el.freeTransform.hideHandles();
});
this.freeTransform.showHandles();
});
}
};
function dragmove(dx, dy, x, y, e) {
this.transform(this.current_transform+'T'+dx+','+dy);
}
function dragstart(x, y, e) {
this.current_transform = this.transform();
}
function dragend(e) {
this.current_transform = this.transform();
};
var elements = [];
$scope.raph = function () {
c= paper.circle(40,40,40).click(function() {
someFunction(this);
$scope.current= this;
});
r = paper.rect(50, 60, 100,100,25).click(function() {
someFunction(this);
$scope.current= this;
});
d = paper.rect(70, 60, 100,100,25).click(function() {
someFunction(this);
$scope.current= this;
});
elements.push(c,r,d);
c.attr({
fill:'red',
cursor:'pointer'
});
r.attr({
fill:'green',
cursor:'pointer'
});
d.attr({
fill:'blue',
cursor:'pointer'
});
};
$scope.back = function () {
$scope.current.toBack();
}
$scope.front = function () {
$scope.current.toFront();
}
$scope.clear = function () {
paper.clear();
};
$scope.flips = function () {
$scope.current.rotate(180);
};
//function for cloning element
$scope.clones = function () {
var n = $scope.current.clone();
n.attr("x",$scope.current.attr("x")+100);
$scope.current = n;
elements.push(n);
n.click(function() {
someFunction(this);
});
};
$scope.changing= function (e) {
$scope.opacity=event.target.value;
if($scope.current != null){
$scope.current.attr({
opacity:$scope.opacity
})
}
}

UPDATE: just use Fabric.js http://fabricjs.com/kitchensink/, although you'll probably need to add Darkroom http://mattketmo.github.io/darkroomjs/ for cropping.
Some suggestions for RaphaelJS:
resize: http://raphaeljs.com/reference.html#Element.transform c.transform('S1.5,1.5')
flip: resize with a negative scale in either direction (S-1,1)
drag: http://raphaeljs.com/reference.html#Element.drag, also consider set overloads like http://wesleytodd.com/2013/4/drag-n-drop-in-raphael-js.html
changing colors and stuff: http://raphaeljs.com/reference.html#Element.attr
undo -- you'll have to save a list of operations/previous states to reverse?
crop -- most likely you'd create the UI in raphael using a mask/overlay (layers: http://raphaeljs.com/reference.html#Element.toBack) to send instructions to a server-side API to handle the heavy work. You can manipulate raster images in the browser, but if people are uploading photos it could quickly overload the memory limitations (think: unedited 12MP photo)
Check out stuff like Sketchpad http://ianli.com/sketchpad/ or the commented Darkroom http://mattketmo.github.io/darkroomjs/ for ideas.
You can always try exploring the sourcecode for existing web editors, like Google+... (that was mostly a joke).

Related

How to add an event handler function to a prototype in javascript?

Thanks to all who helped me out earlier tonight with my little project.
I am attempting to re-write a simple procedural program I wrote a few weeks ago using OOP javascript. The program is a reaction tester that presents a random shape to the user and measures how quickly the user clicks the shape and presents the speed. Earlier I finally managed to actually get a randomly sized and colored square to appear on the page. Now I am trying to write an event handler function that will set the css display property to none when the shape is clicked so that the shape disappears. However, the event handler function doesn't work and I have tried it a few different ways so far. See my entire code below:
function Shape () {
this.x = Math.floor(Math.random()*1200);
this.y = Math.floor(Math.random()*500);
this.draw();
}
Shape.prototype.draw = function() {
var shapeHtml = '<div id="shape-div"></div>';
var widthAndHeight = Math.floor(Math.random()*400);
this.shapeElement = $(shapeHtml);
this.shapeElement.css({
'width': widthAndHeight,
'height': widthAndHeight,
position: "relative",
left: this.x,
top: this.y
});
this.shapeElement.css({
display: "block"
});
//Just below is where I am trying to create a function to make the shape disappear when clicked
this.shapeElement.click(function() {
this.shapeElement.css("display", "none");
});
$("body").append(this.shapeElement);
}
"use strict";
Shape.prototype.colour = function() {
var colours = '0123456789ABCDEF'.split('');
var randomColour = "#";
for (i = 0; i < 6; i++) {
randomColour+=colours[Math.floor(Math.random()*16)];
};
this.shapeElement.css({backgroundColor: randomColour});
}
$(document).ready(function() {
var square = new Shape();
square.draw();
square.colour();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This just won't work. I am making the transition to OOP and finding it really difficult to do things that were a cinch using procedural programming. Is this typical? Thanks again for the help.
You could try replasing:
this.shapeElement.click(function() {...
for
this.shapeElement.on("click",function() {...
You are adding this element to the DOM after it loads.
Also, check your console, because inside the event listener this.shapeElement.css("display", "none"); probably gives you an error, this in that context is the element calling the event... I believe you could use:
$(this).css({"display": "none"});
Whenever you use function(){, the inside of that function acquires a new calling context (a new this) depending on how it was called. But in your handler, you don't want a new this value - you want to inherit the this of the instantiated Shape. You can use an arrow function instead, or you can remember that this on a handler references the clicked element:
function Shape () {
this.x = Math.floor(Math.random()*1200);
this.y = Math.floor(Math.random()*500);
this.draw();
}
Shape.prototype.draw = function() {
var shapeHtml = '<div id="shape-div"></div>';
var widthAndHeight = Math.floor(Math.random()*400);
this.shapeElement = $(shapeHtml);
this.shapeElement.css({
'width': widthAndHeight,
'height': widthAndHeight,
position: "relative",
left: this.x,
top: this.y
});
this.shapeElement.css({
display: "block"
});
//Just below is where I am trying to create a function to make the shape disappear when clicked
this.shapeElement.click(function() {
$(this).css("display", "none");
});
$("body").append(this.shapeElement);
}
"use strict";
Shape.prototype.colour = function() {
var colours = '0123456789ABCDEF'.split('');
var randomColour = "#";
for (i = 0; i < 6; i++) {
randomColour+=colours[Math.floor(Math.random()*16)];
};
this.shapeElement.css({backgroundColor: randomColour});
}
$(document).ready(function() {
var square = new Shape();
square.draw();
square.colour();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

How to copy canvas image data into a paper.js Raster object

I'm trying to add Caman.js filter functionality to a paper.js Raster. And in order not to loose image information I want to reset the image data to the original data (_camanImageData) every time before I call Caman by copying it from a cloned raster. The code is like this:
with(paper) {
Raster.prototype._camanImageData = null;
Raster.prototype.filter = function(options) {
var self = this,
ctx = this.getContext(), // caution! This also inits this._canvas (should).
size = this._size;
if( !this._camanOrgImage ) {
this._camanOrgImage = this.clone(false);
} else {
var dst = ctx.createImageData(size.width, size.height);
dst.data.set(this._camanOrgImage.getImageData(new Rectangle(0, 0, size.width, size.height)).data);
this.setImageData(dst);
}
Caman(this._canvas, function () {
for( option in options ) {
var value = options[option];
this[option](value);
}
this.render(function () {
self._changed(129);
});
});
};
}
The first call works fine. The second works apparently on the already modified data.
I call the filter function like this:
raster.filter({
brightness: 50,
saturation: 10,
hue: 20,
});
What do I do wrong?
The problem was with Caman. Caman doesn't reload the changed canvas, if a "data-caman-id" attribute is set in the canvas element. Just delete this before the call to Caman and it works fine:
this._canvas.removeAttribute("data-caman-id");
Caman(this._canvas, function () {
...
The solution came from: How can I change out an image using CamanJS?

Changing cursor to pointer on roll over in HTML5 canvas with Flash CC

I'm working on adding some interactivity to some buttons in an HTML5 canvas document in FLASH CC. The functionality works great, but I can't figure out how to add the JS to it for adding a rollover so that the handcursor/pointer appears when the buttons are rolled over.
I know how to do this in AS3 through the buttonMode, but I'm a total noob to JS.
Below is the code that I have currently for the buttons in the HTMl5 canvas doc, Thanks in advance for any help!
var frequency = 1;
stage.enableMouseOver(frequency);
this.btn_yes.addEventListener("click", clickYes.bind(this));
this.btn_no.addEventListener("click", clickNo.bind(this));
this.btn_yes.addEventListener("mouseover", MouseOverYes);
this.btn_yes.addEventListener("mouseout", MouseOutYes);
this.btn_no.addEventListener("mouseover", MouseOverNo);
this.btn_no.addEventListener("mouseout", MouseOutNo);
function clickYes()
{
this.gotoAndPlay("chooseYes");
}
function clickNo()
{
this.gotoAndPlay("no");
}
function MouseOverYes()
{
this.btn_yes.style.cursor = 'pointer';
}
function MouseOutYes()
{
this.btn_yes.style.cursor = 'default';
}
function MouseOverNo()
{
this.btn_no.style.cursor = 'pointer';
}
function MouseOutNo()
{
this.btn_no.style.cursor = 'default';
}
Maybe try this one.
stage.enableMouseOver();
var root = this;
root.theButtonName.cursor = "pointer";
stage.canvas.style.cursor = "default";
will set the mouse cursor to it's normal state.
stage.canvas.style.cursor = "none";
This will make the mouse cursor disappear, there's a serious lack of documentation on everything right now. Trying to find a good resource for coding in flash canvas as well. if you find one please let me know.
#user3570797
If you are using FlashCC, this can be done easily by creating button symbols. Try something like this: http://grab.by/Ktw2
Then reference these buttons by name via the 'ExportRoot' object and attach event handlers to those buttons.
Example:
function init() {
canvas = document.getElementById("canvas");
exportRoot = new lib.Button();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
stage.enableMouseOver();
var yesBtn = exportRoot.yesBtn;
var noBtn = exportRoot.noBtn;
yesBtn.addEventListener("click", handleClick);
noBtn.addEventListener("click", handleClick);
createjs.Ticker.setFPS(lib.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
}
function handleClick(event) {
//doSomething...
}

cc.sprite.create not working? cocos2d-html

I am new to Cocos2d-html5 v2.2.1 and I am trying to add a sprite (simple image) to the canvas. The code does add an image with the correct width and height but it is just black.
I cant seem to find any errors so I am kinda stuck.
Any help would be great. My code is below:
In main.js i load the resources:
applicationDidFinishLaunching:function () {
if(cc.RenderDoesnotSupport()){
//show Information to user
alert("Browser doesn't support WebGL");
return false;
}
// initialize director
var director = cc.Director.getInstance();
//cc.EGLView.getInstance()._adjustSizeToBrowser();
//cc.EGLView.getInstance()._resizeWithBrowserSize(true);
//cc.EGLView.getInstance().setDesignResolutionSize(600, 400, cc.RESOLUTION_POLICY.SHOW_ALL);
// set FPS. the default value is 1.0/60 if you don't call this
director.setAnimationInterval(1.0 / this.config['frameRate']);
//load resources
cc.LoaderScene.preload(g_resources, function () {
director.replaceScene(new this.startScene());
}, this);
return true;
}
g_resources is defined in resource.js:
var s_jet = "images/jet.png";
var s_character = "images/p1_front.png";
var g_resources = [
//image
{src:s_jet},
{src:s_character}];
spriteScene.js:
init:function () {
var selfPointer = this;
this._super();
var size = cc.Director.getInstance().getWinSize();
var lazyLayer = cc.LayerColor.create(new cc.Color4B(45, 50, 128, 255), 600, 600);
//var lazyLayer = cc.Layer.create();
lazyLayer.setAnchorPoint(new cc.Point(0.5,0.5));
var characterSprite = cc.Sprite.create("./images/p1_front.png");
lazyLayer.addChild(characterSprite, 0);
this.addChild(lazyLayer);
var rotateToA = cc.RotateTo.create(2, 0);
var scaleToA = cc.ScaleTo.create(2, 1, 1);
characterSprite.setPosition(new cc.Point(size.width/2,size.height/2));
I just dont understand why the sprite is just being drawn as a black box.
Edit: When I uncomment the line below from main.js; at first the image is still black but when I resize the browser, the image appears:-
cc.EGLView.getInstance().setDesignResolutionSize(600, 400, cc.RESOLUTION_POLICY.SHOW_ALL);
I dont know what this is implying.
Ok guys! I figured it out and I am pissed. Changed this line:
this.characterSprite.setPosition(new cc.Point(size.width/2,size.height/2));
to:
this.characterSprite.setPosition(cc.p(size.width/2, size.height/2));
Documentation/tutorials for cocos2d-html5 are outdated and this is getting frustrating.

openlayers pan zoom bar modification

i am new at openlayers and i want to learn how can i add pan zoom bar like http://maps.cloudmade.com/ zoombar. it shows some tag as building , country etc. when you on this pan...
You can override OpenLayers.Control.PanZoom, for example:
idecan.PanZoom = OpenLayers.Class(OpenLayers.Control.PanZoom, {
bounds: null,
initialize: function(options) {
this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
OpenLayers.Control.PanZoom.Y);
if (arguments[0].bounds)
this.bounds = arguments[0].bounds;
OpenLayers.Control.prototype.initialize.apply(this, arguments);
},
includeButtons: {
"zoomin": {
outImageSrc: "zoom-plus-mini.png",
overImageSrc: "zoom-plus-mini-over.png"
},
"zoomout": {
outImageSrc: "zoom-minus-mini.png",
overImageSrc: "zoom-minus-mini-over.png"
},
"zoomworld": {
outImageSrc: "zoom-world-mini.png",
overImageSrc: "zoom-world-mini-over.png"
}
},
makeMouseCallback: function(id, state) {
var selector = state + "ImageSrc";
var src = OpenLayers.Util.getImagesLocation() + this.includeButtons[id][selector];
return function(evt) {
var img = this.firstChild;
if (img.src != src) {
img.src = src;
}
};
},
_addButton: function(id, img, xy, sz) {
if (id in this.includeButtons) {
var src = this.includeButtons[id].outImageSrc;
var size = new OpenLayers.Size(18,18);
var btn = OpenLayers.Control.PanZoom.prototype._addButton.call(this, id, src, xy, size);
if (this.bounds) {
var bounds = this.bounds;
var getBounds = function() {
return bounds;
};
btn.getBounds = getBounds;
}
btn.className = this.displayClass + id.capitalize();
btn._btnId = id;
OpenLayers.Event.observe(btn, "mouseover", OpenLayers.Function.bindAsEventListener(this.makeMouseCallback(id, "over"), btn));
OpenLayers.Event.observe(btn, "mouseout", OpenLayers.Function.bindAsEventListener(this.makeMouseCallback(id, "out"), btn));
return btn;
}
},
buttonDown: function (evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
switch (this.action) {
case "panup":
this.map.pan(0, -this.getSlideFactor("h"));
break;
case "pandown":
this.map.pan(0, this.getSlideFactor("h"));
break;
case "panleft":
this.map.pan(-this.getSlideFactor("w"), 0);
break;
case "panright":
this.map.pan(this.getSlideFactor("w"), 0);
break;
case "zoomin":
this.map.zoomIn();
break;
case "zoomout":
this.map.zoomOut();
break;
case "zoomworld":
if (map.center)
map.setCenter(idecan.center, idecan.zoom);
//this.map.zoomToExtent(this.getBounds());
else
this.map.zoomToMaxExtent();
break;
}
OpenLayers.Event.stop(evt);
},
CLASS_NAME: "idecan.PanZoom"
});
I'm also currently implementing a customized derivate of OpenLayer's PanZoom control. Unfortunately, the PanZoom control contains a lot of cruft code, many lines are just devoted to some hard-coded control styling that could be done much easier in CSS (e.g. why must every control button be 18x18 pixels?)
Felix' answer is a good starting point, but if you don't need to implement your PanZoom control super urgent, I'd wait until OpenLayers 2.11 or some newer version is out. The OpenLayers devs are currently refactoring many of the hard coded formatting to CSS, making the control code slimmer and more easy to understand.

Categories