I' ve question.
I'm coding an editor like this, and I also want a ruler to add it (which change values with mouse-scroll (zoom in and out) )
But I can't do it. I tried all rulers on github but they are running only body tag.
Unfortunately, there are no documents for zoom in or zoom out.
I'm new with HTML5 canvas and I'm stuck. Waiting for your helps. Thanks!
One approach would be to have three separate canvases, one as the main canvas and one each for the top/left rulers.
When zooming in/out, you set the zoom level on the main canvas, but you will need to redraw the rulers manually, here is a really simple example:
function redrawRulers() {
topRuler.clear();
leftRuler.clear();
topRuler.setBackgroundColor('#aaa');
leftRuler.setBackgroundColor('#aaa');
zoomLevel = mainCanvas.getZoom();
for (i = 0; i < 600; i += (10 * zoomLevel)) {
var topLine = new fabric.Line([i, 25, i, 50], {
stroke: 'black',
strokeWidth: 1
});
topRuler.add(topLine);
var leftLine = new fabric.Line([25, i, 50, i], {
stroke: 'black',
strokeWidth: 1
});
leftRuler.add(leftLine);
}}
Fiddle
UPDATE: For the rulers, here's some very simple code to draw figures on the top ruler (fiddle also updated):
// Numbers
for (i = 0; i < 600; i += (100 * zoomLevel)) {
var text = new fabric.Text((Math.round(i / zoomLevel)).toString(), {
left: i,
top: 10,
fontSize: 8
});
topRuler.add(text);
}
Now, of course you will want to convert those numbers into whatever units are appropriate for your application. Also, you may want to consider drawing the numbers at more frequent intervals when you're zoomed in, and to space them out more when you're zoomed out. But I think I've given you enough to get you going here.
Add the below code in document.ready this will bind a mouse scroll event with the canvas and hence the zoom in and out will happen when you use mousewheel.
$(mainCanvas.wrapperEl).on('mousewheel', function(e) {
var dir = e.originalEvent.wheelDelta;
if (dir > 0){
var ZoomValue = mainCanvas.getZoom() * 1.2;
} else {
var ZoomValue = mainCanvas.getZoom() * .83333333333333333;
}
redrawRulers();
mainCanvas.setZoom(ZoomValue, e);
e.originalEvent.returnValue = false;
});
Updated Fiddle For Mouse Scroll
Related
I can't seem to get any output from the project I'm working on with paper.js
Doesn't seem to recognise raster.on as a function for some reason... Could anyone help me as to why the error is being displayed says not a function?
I've tried initialising the paperscript in a couple different ways (or what I thought I have - as very much a newbie).
Any help would be VERY much appreciated, been trying this last 3-4 days with no luck and a lot of trailing and error with no joy... boooooo :'(
Please see js fiddle below:
js fiddle - raster.on not a function
/*Paper JS Setup for working in CodePen */
/* ====================== *
* 0. Initiate Canvas *
* ====================== */
// expose paperjs classes into global scope
paper.install(window);
// bind paper to the canvas
paper.setup('canvas');
// Only executed our code once the DOM is ready.
window.onload = function() {
// Get a reference to the canvas object
var canvas = document.getElementById('myCanvas');
// Create a raster item using the image tag with id='mona'
var raster = new Raster('mona');
// Hide the raster:
raster.visible = false;
// The size of our grid cells:
var gridSize = 12;
// Space the cells by 120%:
var spacing = 1.2;
// As the web is asynchronous, we need to wait for the raster to load
// before we can perform any operation on its pixels.
raster.on('load', function() {
// Since the example image we're using is much too large,
// and therefore has way too many pixels, lets downsize it to
// 40 pixels wide and 30 pixels high:
raster.size = new Size(40, 30);
for (var y = 0; y < raster.height; y++) {
for(var x = 0; x < raster.width; x++) {
// Get the color of the pixel:
var color = raster.getPixel(x, y);
// Create a circle shaped path:
var path = new Path.Circle({
center: new Point(x, y) * gridSize,
radius: gridSize / 2 / spacing,
fillColor: 'black'
});
// Scale the path by the amount of gray in the pixel color:
path.scale(1 - color.gray);
}
}
// Move the active layer to the center of the view, so all
// the created paths in it appear centered.
project.activeLayer.position = view.center;
});
}
As explained in the documentation for raster, you should use raster.onLoad attribute:
raster.onLoad = function() {
console.log('The image has loaded.');
};
I have taken over an Angular project and having some trouble with the Leaflet JS library. Specifically, there are icons you can click from a toolbar and then click to place them on a map. There is a sidebar menu where you can click the icon size you want - Small (20x20), Medium (41x41), or Large (60x60). The issue is that when changing between sizes, the center point of the placed icon(s) is not maintained. Here is the code that is executed when the Large button is clicked (the Medium size is default when the page loads):
$(document).on("click", ".largeicon", function () {
console.log("Large clicked!");
drawnItems.eachLayer(function (layer) {
if (layer instanceof L.Marker) {
if (layer.dragging) {
type_marker = layer._icon.classList[1];
L.Icon.Large = L.Icon.Default.extend({
options: {
iconSize: new L.Point(60, 60),
iconUrl: layer.dragging._marker._icon.currentSrc,
iconRetinaUrl: layer.dragging._marker._icon.currentSrc,
}
});
var large = new L.Icon.Large();
//console.log("new L.Icon.Large", large);
if (map.getBounds().contains(layer.getLatLng())) {
layer.setIcon(large);
layer._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = '30px 30px 0px';
}
}
}
});
L.Icon.Default.prototype.options.iconSize = [60, 60];
pinsize = 60;
});
}
And I have a function that (should) properly calculate the new position of the marker so that the same center point is maintained:
function getNewLatLng(style, originalLat, originalLng, newWidth, newHeight) {
var originalWidth = style["width"].slice(0, -2);
var originalHeight = style["height"].slice(0, -2);
var newLat = originalLat - ((newWidth - originalWidth) / 2);
var newLng = originalLng - ((newHeight - originalHeight) / 2);
return {
newLat: newLat,
newLng: newLng
};
}
Using console logging, it appears that this method calculates the new offsets correctly, but whenever I click between the Large and Medium sizes, the marker slowly moves across the map with each switch.
Can anyone provide some help/guidance on how I can set the updated marker position so that the same center point is maintained between size changes?
Thank you in advance.
So, after much trial and error, I finally figured out how to keep the same center point of the markers. I needed to update the code to this:
if (map.getBounds().contains(layer.getLatLng())) {
layer.setIcon(large);
layer._icon.style[L.DomUtil.TRANSFORM] = "translate3d(" + nc.newX + "px," + nc.newY + "px,0px) rotateZ(0deg)";
layer._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = '30px 30px 0px';
}
Afer adding the [L.DomUtil.TRANSFORM] = "translate3d... line, the centers were properly maintained when changing the mark size from any size to any other size.
I want to replicate the basic functionality of a free transform tool (no rotation), by dragging on the border of a easeljs Shape and adjusting the container to match it. I'm currently using the scaleX and scaleY properties and it sort of works but is not quite right.
If you do one scaling transformation it works pretty well. However if you release, then do another scaling transformation, it jumps very glitchily, and can occasionally break sending the x/y coordinates all the way to stage 0. Any help on this issue would be great!
http://jsfiddle.net/frozensoviet/dsczvrpw/13/
//circle
var circle = new createjs.Shape(new createjs.Graphics()
.beginFill("#b2ffb2")
.drawCircle(0, 0, 50));
circle.setBounds(0, 0, 50, 50);
//create the border as a seperate object
var cBorder = new createjs.Shape(new createjs.Graphics().setStrokeStyle(10)
.beginStroke("#000").drawCircle(0, 0, 50));
cBorder.setBounds(0, 0, 50, 50);
//add both to the container
circleContainer.addChild(circle);
circleContainer.addChild(cBorder);
var cWidth = circleContainer.getBounds().width;
var cHeight = circleContainer.getBounds().height;
//find initial mouse position relative to circle center
cBorder.on("mousedown", function (evt) {
//initial mouse pos
this.initial = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
});
//set the relevant circle axis scale to ratio of mouse pos/initial mouse pos
cBorder.on("pressmove", function (evt) {
//current moouse pos
this.offset = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
if (this.initial.x > this.initial.y) {
//sides
circleContainer.scaleX = this.offset.x / this.initial.x;
} else if (this.initial.x < this.initial.y) {
//top/bottom
circleContainer.scaleY = this.offset.y / this.initial.y;
} else {
//diagonals
circleContainer.scaleX = this.offset.x / this.initial.x;
circleContainer.scaleY = this.offset.y / this.initial.y;
}
stage.update();
});
The issue is your initial calculations don't account for the change in the scale of the circle. You would have to transform the coordinates using localToGlobal. Fortunately, there is an even easier way:
this.initial = {
x: Math.abs(evt.localX),
y: Math.abs(evt.localY)
};
You can also turn on ignoreScale on the border, which makes it not stretch:
createjs.Graphics().setStrokeStyle(10,null,null,null,true) // The 5th argument
Lastly, your bounds setting might work for your demo, but it is not correct. Your circle draws from the center, so it should be:
cBorder.setBounds(-25, -25, 50, 50);
Here is an updated fiddle: http://jsfiddle.net/tfy1sjnj/3/
Hello I use fabricjs to play with the html canvas.
I create the canvas and i add group of objects on it.
On a group of objects, I need to keep fixed width & height for some objects while I scale the object.
I use the 'object:scaling' event to get the active object when it changes size, I read each object of the group and I assign element[i].set({'radius':5}) on the group objects that I want to be unchanged.
But the result is that , all the group object to resize.
I show you the snippet of the object:scaling event :
canvas.on('object:scaling',function(e){
var activeObject1 = e.target;
var elements = e.target._objects;
var count_elements = elements.length;
for(var i = 0; i < count_elements; i++) {
var type = elements[i].typeCircle;
if(type == "parts"){
//elements[i].set({"radius":8,"fill":"#abcde2","stroke":"#367827"});
//elements[i].set('radius',8);
/*elements[i].setWidth(16);
elements[i].setHeight(16);
elements[i].currentWidth = 16;
elements[i].currentHeight = 16;
elements[i].scaleX = 1;
elements[i].scaleY = 1;
console.log(elements[i]);
canvas.renderAll();*/
}
}
});
What should I write into the for loop to keep fixed size on some objects?
everything that I used above, they don't work except for the "fill":"#abcde2","stroke":"#367827"
If anyone has faced something similar on fabricjs, please let me know .
You must use setScaleX() and setScaleY() methods.
Here is an example...
var Rect = new fabric.Rect({
width: 200,
height: 200,
top: 100,
left: 100,
fill: 'rgba(255,0,0,0.5)',
stroke: '#000',
strokeWidth: 1,
});
var Circle = new fabric.Circle({
left: 100,
top: 100,
fill: '#FF00FF',
stroke: 'red',
radius: 100,
opacity: 1,
});
var Group = new fabric.Group([Rect, Circle])
canvas.add(Group)
canvas.on({
'object:scaling': onChange
})
function onChange(obj) {
var circle = obj.target.item(1),
group = obj.target,
scaleX = circle.width / group.getWidth(),
scaleY = circle.height / group.getHeight();
circle.setScaleX(scaleX);
circle.setScaleY(scaleY);
}
JSFIDDLE
Using fabricjs2 to stop scaling of a text item I have modified Rafik Avtoyan's function to work well. I have locked Y scaling for my group so you will have to add this if required.
function handleScalingEvent(obj) {
var text = obj.target.item(1),
group = obj.target,
scaleX = group.width / (group.width * group.scaleX);
text.set('scaleX', scaleX);
}
Best way to make the objects the same size is to divide desired width by object width. For example, if you want all added objects to be 256px the code will look this way:
function selectImage(imagePath){
fabric.Image.fromURL(imagePath, function(oImg) {
canvas.add(oImg);
let zoom = 256 / oImg.width;
oImg.set({ scaleX: zoom, scaleY: zoom, });
oImg.center();
});
}
In the case when the image will be 64px it will scale it by 4 to 256px and in case the image is 512px it will scale it by 0.5 which will make the image smaller to 256px.
I have ScrollView and sequence of Surface elements in it.
I want to be able to scroll to the latest element as soon as it appears in the array.
Here is a demo, I want it to be scrolled just like in any chat application... old surfaces go beyond the scene, and new will be always at the bottom. The question is the same as here: Famo.us how to make a scrollview be filled from bottom to top (or right to left)?
http://jsfiddle.net/LA49a/
Famous.loaded(function () {
var Engine = Famous.Core.Engine;
var Surface = Famous.Core.Surface;
var Scrollview = Famous.Views.Scrollview;
var Timer = Famous.Utilities.Timer;
var mainContext = Engine.createContext();
var scrollview = new Scrollview();
var surfaces = [];
var i = 0;
scrollview.sequenceFrom(surfaces);
Timer.setInterval(function () {
var temp = new Surface({
content: "Surface: " + (i + 1),
size: [undefined, 50],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: "50px",
textAlign: "center"
}
});
temp.pipe(scrollview);
surfaces.push(temp);
i++;
// scrollview.goToNextPage();
}, 400);
mainContext.add(scrollview);
});
This is supported in the famous-flex ScrollView by setting the alignment property to 1.
See this demo:
https://github.com/IjzerenHein/famous-flex-chat
I'm assuming you're putting in a very large, round number to get the scrollview to move to the end, which shows your 'latest' element.
If you've got surfaces that are the same width (assuming an X orientation of the scrollview) you might get a smoother result by checking for the actual number of pixels you need to move to position the last item at the right edge of scrollview. You have the original array that you ran sequenceFrom() on. Multiply the number of items in that by your surface width and subtract pixels for the scrollview's width (which could be set to 'undefined' in which case you'll need to look at the window's width) to adjust for getting the latest element to the right side of the scrollview instead of the left side.
If your surfaces are not the same size, you might change things to track the x-offset of each surface as you add them to the backing array by just adding a property to the surface. Then you could just ask the surface what its offset is and subtract the right amount for the width of the scrollview.
Hope that helps you get closer.
Solved, by rotating ScrollView by 180˚ and all surfaces inside it also by 180˚ http://jsfiddle.net/tamtamchik/LA49a/3/
Famous.loaded(function () {
var Engine = Famous.Core.Engine;
var Surface = Famous.Core.Surface;
var Scrollview = Famous.Views.Scrollview;
var Timer = Famous.Utilities.Timer;
var Transform = Famous.Core.Transform;
var StateModifier = Famous.Modifiers.StateModifier;
var ContainerSurface = Famous.Surfaces.ContainerSurface;
var mainContext = Engine.createContext();
var scrollview = new Scrollview();
var surfaces = [];
var i = 0;
var surfaceHeight = 50;
scrollview.sequenceFrom(surfaces);
Timer.setInterval(function () {
var container = new ContainerSurface({
size: [undefined, surfaceHeight]
});
var temp = new Surface({
content: "Surface: " + (i + 1),
size: [undefined, surfaceHeight],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: "50px",
textAlign: "center"
}
});
var surfaceRotate = new StateModifier({
transform: Transform.rotateZ(Math.PI)
});
var surfacePush = new StateModifier({
transform: Transform.translate(window.innerWidth, surfaceHeight)
});
container.add(surfacePush).add(surfaceRotate).add(temp);
container.pipe(scrollview);
temp.pipe(scrollview);
surfaces.unshift(container);
i++;
}, 400);
var rotate = new StateModifier({
transform: Transform.rotateZ(Math.PI)
});
var push = new StateModifier({
transform: Transform.translate(window.innerWidth, window.innerHeight)
});
mainContext.add(push).add(rotate).add(scrollview);
});
hmm, that's pretty crafty! Its a couple of extra matrices which could get very slow if you have a lot of text content in those nodes tho...
By using a huge number, this is just to force famo.us to scroll to the end of the view, I assume?
if the render had happened you could also:
pos = getPosition(nodeNum) // for last elements scrollposition
setPosition(pos)
But if the insert happens in same code block as your movement control it's not rendered yet.
There's a complicated workaround offered here using events to trigger the sizing calculation after the deploy/render has occurred:
how can we get the size of a surface within famo.us?
and another trick here to make famous recalculate sizes from the inimitable #johntraver
how to make famo.us recalculate sizes?
did you also look at Engine.nextTick() or JQuery.deferred ?
It seems that immediately after you insert the content there will be a "not rendered" instant but after an update you could get the sizes OK.