KineticJS - new Shape from String or Array - javascript

I have several shape definitions stored in files, i.e.
Kinetic.Rect({width : 150,height : 50,stroke : 'black',fill :'#00D2FF',strokeWidth : .5,cornerRadius : 25,name : 'rect'});
This line (among with others) are available through an array. Normally I create this shape as follows:
rect = new Kinetic.Rect({
width: 150,
height: 50,
stroke: 'black',
fill: fill,
strokeWidth: .5,
cornerRadius: 25,
name: 'rect'
});
How I create this shape from an array/string?
(rect = new "string from array[xx]")?

KineticJS shapes (like Rect) are defined using a plain javascript object containing your desired properties.
Note: you're fill definition relies on a variable fill. Be sure fill is valid!
// define a rectangle
var rectDefinition1={
width: 150,
height: 50,
stroke: 'black',
strokeWidth: .5,
cornerRadius: 25
};
You can feed this definition into a function like this that creates a Kinetic.Rect from your definition and applies a specific new name to that object.
// function to create a Kinetic.Rect from the rectangle definition
function makeRectFromObject(myObject,newName,newFill){
var rect=new Kinetic.Rect(myObject);
rect.name(newName);
rect.fill(newFill);
return(rect);
}
You would use the creation function like this:
// Use like this
var rect1=makeRectFromObject(rectDefinition1,"rect1","red");
// add the newly created rect to the layer and redisplay the layer
layer.add(rect1);
layer.draw();
If you want to store and retrieve your object definitions, you can use JSON:
// serialize your javascript object to a JSON text string
// This is a regular text string that can be stored in a file or database.
var myRectDefinitionAsJSONText = JSON.stringify(rectDefinition1);
// create a javascript object from your JSON text string
var rectDefinition1= JSON.parse( myRectDefinitionAsJSONText );

Related

Control multiple fills and strokes of an SVG with a function

So I've built a map with several different colored lines. I chopped up the lines into a string of segments at each dot.
What I'm trying to do is use javascript to change the colors of the lines and eventually move to being able to change the dynamically with a feed.
I made a color chart that I'm trying to get the line segment object to find its color and change to the proper corresponding color. The dots pulsate the fill color with greensock properly but the lines are giving me trouble. I keep getting an object isn't a function error.
I've tried a few different ways to set up the formula but I'm stuck.
var colorSet = {
'#ff0000': '#ff6666',
'#008250': '#42a680',
'#a3238f': '#bd73b2',
'#0079c2': '#42a0db',
'#ff8c00': '#ffba66',
'#96c800': '#cae087',
'#a86000': '#c28f4e',
'#999999': '#cccccc',
'#ffe000': '#fff399'
}
var animateThis = function(obj) {
var getStroke = obj.getAttribute('stroke');
TweenLite.to(obj, 1.5, {
fill: "#bbbbbb",
yoyo: true,
repeat: -1
});
};
Here is my fiddle: https://jsfiddle.net/Spiderian/hzafm6vk/3/#&togetherjs=6e0qwPovaP
To color lines you need to use stroke, not fill. And on <line> not on <g>.
getStroke is not used in you code. (And there is no getAttribute on $ by the way. There is attr())
colorSet also unused, and I don't really understand what is it for. As all of lines and points in the jsfiddle you've provided are of the same color.
Don't really understand why there are two lines for each <g>.
But nevertheless here is what you need to do:
// change all line selectors from this
// const lsSIT0001 = $("#SIT0001");
// const lsSIT0002 = $("#SIT0002");
// ...
// to this
const lsSIT0001 = $("#SIT0001 line");
const lsSIT0002 = $("#SIT0002 line");
// ...
// because you need to change stroke color of `<line>`s, not `<g>`s
// split
// var itemsToAnimate = [ lsSIT0020, ..., lsSIT0001, SIR4947, ..., SIR4629 ]
// into
const linesToAnimate = [ lsSIT0020, ..., lsSIT0001 ]
const pointsToAnimate = [ SIR4947 , ..., SIR4629 ]
// run separate animations for lines and dots
const animatePoints = obj => TweenLite.to(obj, 1.5, { fill : "#ff0000", yoyo: true, repeat: -1 })
const animateLines = obj => TweenLite.to(obj, 1.5, { stroke: "#0000ff", yoyo: true, repeat: -1 })
linesToAnimate .forEach(animateLines )
pointsToAnimate.forEach(animatePoints)
Update:
To use colorSet you need to either
set style='stroke: some_color' on each <line> and then use it like this:
const style = obj.eq(i).attr('style')
// `i` is the index of a line inside <g> the color of which you want to get
TweenLite.to(obj, 1.5, { stroke: colorSet[style], ... })
In this case colorSet should look like
const colorSet = {
"stroke: #ff0000": '#ff6666',
...
"stroke: #ffe000": '#fff399',
}
or get computed styles with obj.get(i).computedStyleMap().get("stroke").toString()
In this case colorSet should look like
const colorSet = {
"rgb(255, 0, 0)": '#ff6666',
...
"rgb(255, 224, 0)": '#fff399',
}
But it's experimental technology. So probably don't :)
Or better yet - make a map not from color to color but from ids to color:
const colorSet = {
"#SIT0001": '#ff6666',
...
"#SIT0020": '#fff399',
}
Then you won't need to get colors from DOM.

Dynamic styles in OpenLayers 3

In OpenLayers 2 it was possible to extend the style definition with dynamic parts - special functions that calculates a specific style value at render time. Is there an equivalent in OpenLayers 3?
Here sample code from OpenLayers 2:
var stdStyleMap = new OpenLayers.StyleMap({
"default": new OpenLayers.Style({
/* fixed value */
fillOpacity: 0.8,
/* value from server response */
fillColor: "${fillcolor}",
/* value calculated at render time */
pointRadius: "${getPointRadius}",
}, {
context: {
/* function that calculates the point radius */
getPointRadius: function(feature) {
if (feature.attributes && feature.attributes.pointRadius)
return feature.attributes.pointRadius;
else
return 5;
}
}})
});
Here's a good example of using custom styles for polygons from the Openlayers site.
But the following is an example that answers a question i posted... so, yay for both of us... maybe.
// we'd normally pass feature & resolution parameters to the function, but we're going to
// make this dynamic, so we'll return a style function for later use which will take those params.
DynamicStyleFunction = ( function( /* no feat/res yet!*/ ) {
/**
you really only get style are rendered upon simple geometries, not features. features are made of different geometry types, and styleFunctions are passed a feature that has its geometries rendered. in terms of styling vector geometries, you have only a few options. side note: if there's some feature you expect to see on the the map and it's not showing up, you probably haven't properly styled it. Or, maybe it hasn't been put it in a collection that is included in the source layer... which is a hiccup for a different day.
*/
// for any geometry that you want to be rendered, you'll want a style.
var styles = {};
var s = styles;
/**
an ol.layer.Vector or FeatureOverlay, renders those features in its source by applying Styles made of Strokes, Fills, and Images (made of strokes and fills) on top of the simple geometries which make up the features
Stroke styles get applied to ol.geom.GeometryType.LINE_STRING
MULTI_LINE_STRING can get different styling if you want
*/
var strokeLinesWhite = new ol.style.Stroke({
color: [255, 255, 255, 1], // white
width: 5,
})
var whiteLineStyle new ol.style.Style({
stroke: strokeLinesWhite
})
styles[ol.geom.GeometryType.LINE_STRING] = whiteLineStyle
/**
Polygon styles get applied to ol.geom.GeometryType.POLYGON
Polygons are gonna get filled. They also have Lines... so they can take stroke
*/
var fillPolygonBlue = new ol.style.Style({
fill: new ol.style.Fill({
color: [0, 153, 255, 1], // blue
})
})
var whiteOutlinedBluePolygon = new ol.style.Style({
stroke: strokeLinesWhite,
fill: fillPolygonBlue,
})
styles[ol.geom.GeometryType.POLYGON] = fillPolygonBlue
/**
Circle styles get applied to ol.geom.GeometryType.POINT
They're made with a radius and a fill, and the edge gets stroked...
*/
var smallRedCircleStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: '#FF0000', // red... but i had to look it up
})
})
})
var whiteBigCircleWithBlueBorderStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
fill: new ol.style.Fill({
color: '#FFFFFF' // i guessed it
})
}),
stroke: new.ol.style.Stroke({
color: '#0000FF', // blue
width: 5
})
})
// render all points as small red circles
styles[ol.geom.GeometryType.POINT] = smallRedCircleStyle
// if you pass an array as the style argument, every rendering of the feature will apply every defined style style rendered with the geometry as the argument. that can be a whole lot of rendering in a FeatureOverlay...
smallRedCircleStyle.setZIndex(Infinity)
whiteBigCircleWithBlueBorderStyle.setZIndex(Infinity -1) // that prob wouldn't work, but i hope it's instructive that you can tinker with styles
// so...
var bullseyePointStyle = [ smallRedCircleStyle, whiteBigCircleWithBlueBorderStyle ];
return function dynamicStyleFunction (feature, resolution){
// this is the actual function getting invoked on each function call
// do whatever you want with the feature/resolution.
if (Array.indexOf(feature.getKeys('thisIsOurBullseyeNode') > -1) {
return bullseyePointStyle
} else if (feature.getGeometryName('whiteBlueBox')){
return whiteOutlinedBluePolygon
} else {
return styles[feature.getGeometryName()]
}
}
})()
Yes there is style function that takes a feature and the current resolution, see some examples in the workshop: http://openlayers.org/workshop/vector/style.html

Javascript associative array: pushing an object rewrites other array elements

I have an Object that describes my document. Each document contains layers, that described in Objects in an array
var example = {
id: 'some_id',
layers: [{
id: 'some_layer_id',
width: 100,
height: 200
}]
};
There are different types of layers, each has it's template:
var layer_templates = [{
type: 'text',
width: 100,
height: 100,
style: 'common'
}];
I create new layers in jQuery event:
jQuery('#button').on('click', function () {
var ldata = layer_templates[0]; // get a template
ldata['id'] = 's-l-' + Math.random().toString(); // create an id
example['layers'].push(ldata); // add new "layer" to document
console.log(example['layers']); // check
});
Every time i add new ldata layer to example['layers'] array, it keeps only initial object with id: 'some_layer_id'and rewrites all newly created objects with the last one.
Please check jsFiddle:Problem in action
The problem is that you are not duplicating your template. You are using and re-using the SAME template among all your layers
var ldata = layer_templates[0];
ldata['id'] = 's-l-' + Math.random().toString(); // create an id
in actuality, ldata is just a pointer that is referencing the template, adding the id value to it only modifies your template so it looks like this now:
var layer_templates = [{
type: 'text',
width: 100,
height: 100,
style: 'common',
id: 's-l-0.08636776800267398'
}];
NEXT time you try to add ANOTHER layer, you do the same thing yet again, which only pulls and changes the template once again... in the end, EVERY layer references the same template and therefore is the exact same data.
To fix this, you need to COPY your template, one way to do this is using JSON:
var ldata = JSON.parse(JSON.stringify(layer_templates[0]));
This method creates an entirely new ldata object that is a copy of the template.

recommended way to extend classes in Paper.js

Is there a recommended way to extend classes in Paper.js? In particular, I am interested in extending Path
Pardon if my terminology is incorrect, but I am essentailly asking the same question about paper that is being asked about three here
Based on your comment to the initial version of my answer, you are looking for the 'extend' function (oops, that was exactly what you meant) to do subclassing. In an email to the paper.js mailing list, Jürg Lehni (one of the creators) said:
As for subclassing, that's not something that is supported at the
moment. It might work, it might not, it might work in most cases, but
not in very rare cases that are hard to pinpoint, it might need only a
couple of changes to make it work well, but those might be in many
different places.
For example, each Item subclass has a _type property which is a string
representing its type. Sometimes we check that instead of using
instanceof, because it's faster, and so far, for example for Path we
just assumed there would be no subclassing.
A complication is that there are no paper.Path.Rectangle objects. There are paths, and there are rectangles, but when you call new paper.Path.Rectangle() it creates a new Path using initialization code (createRectangle) that creates a rectangular shape.
So we would need to extend paper.Path. Unfortunately, when you call new paper.Path.Rectangle it calls createPath, which always returns a Path (not your extension). It may be possible to do something like:
var SuperRectangle = paper.Path.extend({
otherFunc: function() {
console.log('dat');
}
});
...and with correctly substituting/overriding for createRectangle or createPath get a subclass to work. Unfortunately, I have not been able to manage it.
My first working recommendation is to make a factory and add your functions to the objects in that factory (jsbin here):
var createSuperRectangle = function(arguments){
var superRect = new paper.Path.Rectangle(arguments);
superRect.otherFunc = function(){
console.log('dat');
}
return superRect;
}
var aRect = new Rectangle(20, 30, 10, 15);
var aPath = createSuperRectangle({
rectangle: aRect,
strokeColor: 'black'
});
aPath.otherFunc();
Similarly, you can use the factory to just change the prototype for your SuperRectangles, having added your functions to that prototype object (and making its prototype the one from paper.Path.__proto__) (jsbin here):
var superRectProto = function(){};
var tempRect = new paper.Path.Rectangle();
tempRect.remove();
superRectProto.__proto__ = tempRect.__proto__;
superRectProto.otherFunc = function(){
console.log('dat');
}
delete tempRect;
var createSuperRectangle = function(arguments){
var superRect = new paper.Path.Rectangle(arguments);
superRect.__proto__ = superRectProto;
return superRect;
}
var aRect = new Rectangle(20, 30, 10, 15);
var aPath = createSuperRectangle({
rectangle: aRect,
strokeColor: 'black'
});
aPath.otherFunc();
Alternatively, you can make an object that encapsulates the Path (jsbin here):
var SuperRectangle = function(arguments){
this.theRect = new paper.Path.Rectangle(arguments);
this.otherFunc = function(){
console.log('dat');
}
}
var aRect = new Rectangle(20, 30, 10, 15);
var aPath = new SuperRectangle({
rectangle: aRect,
strokeColor: 'black'
});
aPath.otherFunc();
aPath.theRect.strokeWidth = 5;
Unfortunately, then to access the path you have to use the theRect variable.
Initial incorrect answer follows:
I don't think you mean "extending classes". In Javascript you can extend objects so that they have more functions, so extending the Path "class" would mean all Path objects have the same new functions. Javascript object extension is further described here.
If I'm wrong, and you do want to extend Path, then you can use:
paper.Path.inject({
yourFunctionName: function(anyArgumentsHere) {
// your function here
}
});
However, I think you are actually talking about creating new objects that mostly behave like Path objects but have different functionality from each other. If that is the case, then you may want to look at this answer about Javascript using prototypical inheritance. For example, here I create two Rectangle objects that behave differently when I ask them to doSomething (jsbin here):
var rect1 = new Path.Rectangle({
point: [0, 10],
size: [100, 100],
strokeColor: 'black'
});
rect1.doSomething = function() {
this.fillColor = new Color('red');
};
var rect2 = new Path.Rectangle({
point: [150, 10],
size: [100, 100],
strokeColor: 'black'
});
rect2.doSomething = function() {
this.strokeWidth *= 10;
};
rect1.doSomething();
rect2.doSomething();
A couple of things.
1) You can wrap the original paperjs object but this is very much a hack
paperjs playground
function SuperSquare() {
this.square = new Path.Rectangle({
size:50,
fillColor:'red',
onFrame:function(base) {
var w2 = paper.view.element.width / 2;
this.position.x = Math.sin(base.time) * w2 + w2;
}
});
}
SuperSquare.prototype.setFillColor = function(str) {
this.square.fillColor = str;
}
var ss = new SuperSquare();
ss.setFillColor('blue');
2) I may clone & create a paper 2017 which operates off of es6 so that you can use the extend keyword.
3) I wrote an application called Flavas but it never gained a following so I just kind of left it. That being said, I have been playing with it lately; upgrading it to es6. With it you can do what you're talking about.
class Square extends com.flanvas.display.DisplayObject {
constructor(size) {
this.graphics.moveTo(this.x, this.y);
this.graphics.lineTo(this.x + size, this.y);
this.graphics.lineTo(this.x + size, this.y + size);
this.graphics.lineTo(this.x, this.y + size);
}
someMethod(param) {
trace("do something else", param);
}
);
I wrote all this kind of quick so feel free to hit me up with Q's.

Dynamically calling Canvas Functions

Is there any way to call canvas functions using apply() or a similar method, as to dynamically call canvas methods or to be able to pass an array of arguments?
Im looking for this effect
context.fillRect.apply(this,args);
If I understand you correctly:
var op = "fillRect";
var args = [
10, 10, 180, 180
];
ctx[op].apply(ctx, args);
Example: http://jsfiddle.net/eZwYQ/
your apply method should work just fine :
function rectangle(){
ctx.fillRect.apply(ctx,arguments);
}
And of course this can get more "dynamic" :
function doSomethingWithCanvas(context,action,arg1,arg2/*,...argn*/){
context[action].apply(context,Array.prototype.slice.call(arguments,2));
}
And you could use the same function to fill a rectangle, to draw a circle or to draw a simple line :
// simple line
doSomethingWithCanvas(ctx,'lineTo',10, 100);
// circle
doSomethingWithCanvas(ctx,'arc',275, 275, 200, 0, Math.PI*2, true);
// fillRect
doSomethingWithCanvas(ctx,'fillRect,10, 10, 180, 180);
PS : by providing the canvas context as an argument, you can use this function to draw on any canvas.

Categories