I am working on a simple editor where you can move text, pictures, and pretty much anything around. It's nothing too complicated, however I am having an issue choosing between using PaperJS and FabricJS - The main reason being the ability to natively transform items (rotate, scale, etc.).
Even though I love PaperJS, I am really considering moving to FabricJS. I create tools for PaperJS like this:
function shapeTool(options) {
this.tool = new Tool();
var options = $.extend({
shape: "circle",
}, options);
this.tool.onMouseUp = function(event) { ... }
...
...
this.tool.activate();
return tool;
}
This is probably the worst way to create them, but for me it's easy to throw all these functions in a file and init my tools: var tool = new shapeTool(options);
Since I will possibly be moving to Fabric, what is the easiest way to make modular tools. In which I can swap between them? (i.e. I have a toolbar and when i click a button my tool changed so i can freedraw, insert images, etc)
Clarification: When I mean a tool, I am talking about a tool in PaperJS. IN Paper, we are able to swap the canvas's active tool. The tool would handle the current mouseDown, KeyDown, etc. events for the canvas. I could write different tools for things like inserting shapes and images, and write one for freedrawing. I could then set the active tool to either one. This would allow me to swap between freedraw and inserting shapes.
Thanks,
Hunter M.
In FabricJS you interact with the behaviour of mouse with events.
Cavas fires:
mouse:down
mouse:move
mouse:up
mouse:over
mouse:out
You register events with ON and OFF:
canvas.on('mouse:down', handler);
You have just on predefined state that is drawingMode true or false
canvas.isDrawingMode = true;
And free drawing starts.
When not drawing the mouse down and up or move can be customized by a property you add to canvs.
canvas.myState = 'addStamps';
Then in your handler:
handler(e) {
switch (canvas.myState) {
case 'deleteItems':
e.target && canvas.remove(e.target);
break;
case 'addStamps':
canvas.add(new fabric.Image('stamp.jpg'));
break;
}
}
You have to do same for mouse:up or mosue:move if you need interaction other than clicks.
This is one of the many ways you can obtain a similar effect.
Related
I have a little problem using Hammer JS with my canvas.
I explain ;
I have a canvas defined like this :
<canvas id="myCanvas"></canvas>
In this canvas, i add some Img :
var s = new CanvasState(document.getElementById('myCanvas');
s.addImg(new Img(100, 0, data.image, data.identifiant));
And , i would like to use Hammer JS to rotate my new Img.
For this, i tried something like :
var hammertime = new Hammer(document.getElementById('myCanvas'));
hammertime.on('pan', function(ev) {
console.log(ev);
});
hammertime.get('pinch').set({ enable: true });
hammertime.get('rotate').set({ enable: true });
I don't know what i should put instead myCanvas. I tried something like myCanvas.selection (the IMG OBJECT) but in vain ..
Any idea ? Any person who already used Hammer JS with a canvas on which we have elements / objects ?
Seems hammerJS don't recognized Objects ..
Thanks
You can use Fabric.js library. It is canvas library, that helps you interact with it much easier. Point is - hammerjs in working very nicely with Fabric.js.
All you need to do, once you have inicialized Fabric.js is this:
const hammertime = new Hammer(canvas.upperCanvasEl);
hammertime.get('pinch').set({ enable: true });
hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
hammertime.on('pan', (ev) => {
alert("pan!");
});
Please note, if you want to enable pinch gesture, you need to enable it.
"canvas" is Fabric.js initialized canvas element.
"upperCanvasEl" is generated by Fabric.js and intercepts all events. Please note, if you use TypeScript that this property is not yet supported by it, so you need to cast it to "any".
The documentation for plotly is a bit hard to use and couldn't find a specific settings for relayout on zoom.
The method I used is to add a 'plotly_relayout' event listener and then check the event data if it has 'xaxis.range[0]'. If it does then I make some update and then do a relayout and restyle.
var gd = document.getElementById(chart_id);
gd.on('plotly_relayout', function (eventdata) {
// Check if a pan or zoom occurred.
if (eventdata.hasOwnProperty('xaxis.range[0]')) {
layout_update = {...,...};
trace_update = {...,...};
Drupal.plotly.relayout(chart_id, layout_update);
Drupal.plotly.restyle(chart_id, trace_update);
}
}
This might be an inefficient way of going about it, though. It seems to be a bit slow. Also it cannot differentiate between a pan and a zoom action as the event data is the same. Is there a better way?
I want to change my cursor when triggering a mousedown event. I found a lot of hints how to achieve that - but these have one major issue. To change the defaultCursorStyle property I need to instantiate the InteractionManager prototype.
var renderer = PIXI.autoDetectRenderer(640, 480),
var interactionManager = new PIXI.interaction.InteractionManager(renderer)
interactionManager.defaultCursorStyle = "crosshair" // a native html/css cursor style
That looks fine at first but the problem here is that this InteractionManager seems to batch-register all events applied to PIXI object via the .on(event, callback) event-binding function per instance.
That means that if there would be a second instance of this InteractionManager, all events would be bound twice and therefore triggered twice. I had this exact issue.
So I needed to revert my changes and try to access the default InteractionManager. Somebody in the HTML5GameDev forum told me to access it like this:
renderer.plugins.interaction
knowing that I tried the following:
renderer.plugins.interaction.defaultCursorStyle = "crosshair"
My events now worked properly again. But the cursor change did not happen. However debugging the line told me that the property defaultCursorStyle was successfully set to "crosshair". Now I'm looking for a way to make this change visible.
My question:
Is there a better way to change the cursor style than the mentioned one above? If no, how can I make my default cursor change visible after setting the new style to the default InteractionManager?
There's a setCursorMode method in the docs, guess it's what you need.
const app = new PIXI.Application({ height, width })
app.renderer.plugins.interaction.cursorStyles.default = 'crosshair'
setTimeout(() => {
app.renderer.plugins.interaction.setCursorMode('pointer')
}, 1000)
Whenever cursor leaves the renderer, PIXI resets cursor mode (here's exactly the line). So you might want to set new cursor mode as default each time you change it.
function changeCursorMode (cursorMode) {
app.renderer.plugins.interaction.cursorStyles.default = cursorMode
app.renderer.plugins.interaction.setCursorMode(cursorMode)
}
app.renderer.plugins.interaction.cursorStyles.crosshair = 'crosshair'
changeCursorMode('crosshair')
I have two images with same dimensions and same positions, but placed in divs with dynamic width depending on user interaction using the jquery beforeAfter plugin.
I would like to enable scroll zooming on these images using wheelzoom, such that zooming on one of these images will zoom the same amount in the same position as the other.
What I am unable to do is this linking of (I suppose) the event handlers along the lines of this:
function onwheel(e){
//adjust image to fit zoom level ...
other_img.onwheel(e);
}
If this is not possible, is it possible to copy the event and change the target image?
I am looking for a solution using either jquery or native Javascript.
Code here (ignore the handle).
EDIT: Any top-level pointers to what should work would also be appreciated
I made a worked example: http://plnkr.co/edit/kH0ec8TVMXUIYlMoBaMq?p=preview
here is the core code:
document.getElementsByClassName("before")[0].addEventListener("wheel", function(event){
if(flag){
flag= false;
return;
};
flag=true;
var newEvent = new WheelEvent("wheel",event);
var elementToTrigger = document.getElementsByClassName("after")[0];
elementToTrigger.dispatchEvent(newEvent);
});
I do something simple, when an event happening("wheel") a trigger the same event to another element and pass as argument the data from the first event to the new event. I use flag variable to deter the custom event trigger again the other event and start an eternal loop.
This is a solution without to edit the source code of plugin. You can do more good solutions if you change the code of wheelzoom.js.
Make the zooming a function that takes enough parameters to handle zooming, and get the values you need from the event handler.
function handleZoom(domNode, zoomDirection, zoomAmount) {
// Do stuff to change the size of `domNode`
}
var img1, img2;
img1 = /* select image node */;
img2 = /* select image node */;
function handleScroll(scrollEvent) {
var direction, amount;
// Use `scrollEvent` to figure out which direction and how far to zoom
handleZoom(img1, direction, amount);
handleZoom(img2, direction, amount);
}
img1.onscroll = handleScroll;
img2.onscroll = handleScroll;
Using OpenLayers, I have a OpenLayers.Control.SelectFeature installed on a layer, with the hover option set to true. When creating the layer I call
<layer>.events.register("featureselected",...)
and
<layer>.events.register("featureunselected",...)
to register functions that create and destroy a popup. This all works fine. Now I want to add a small delay before the popup is created in order to avoid the popup flickering that currently occurs when moving the mouse across multiple features. However, I can't seem to figure out how to do this. I did find the OpenLayers.Handler.Hover handler, which has a delay option, but I don't know how to combine that with the SelectFeature control (if I even can).
I think this post has some valuable info, which I'm about to verify. Some answers down, someone talks about the flickering.
edit: In case you are making your own labels, I noticed the effect is less when you raise the labelOutlineWidth . It seems that only the letters of the label count as 'hover' and not the whole PointRadius radius. When you make the label outline too big, the label looks like a fly that hit a windscreen though (not a square but it follows the label contours, the letters more specifically).
update: apparently this is why when you hover a text label , check this out: pointer events properties. set this attribute (pointerEvents: ) in your OpenLayers.Style and try value 'all' and the others. It sure makes a difference for me.
I bind my feature selections a little different, here's a quick (untested) example that should get you what you need.
var timer,
delay = 500, //delay in ms
hover = new OpenLayers.Control.SelectFeature( <layer> , {
hover: true,
onSelect: function (feature) {
// setup a timer to run select function
timer = window.setTimeout(function () {
// your select code
}, delay);
},
onUnselect: function () {
// first cancel the pending timer (no side effects)
window.clearTimeout(timer);
// your unselect code
}
});
<map>.addControl(hover);
hover.activate();