Collapsing svg group with Raphael.js - javascript

I'm working with svg through Raphael.js and I need to collapse SVG group and show it on click.
I've tried to use 'visibility' attriburte of SVG group and change it on click, but it is ok with hiding but it's not appear back;
const GroupCollapse = function(paper) {
const $svg = paper.canvas; // #mysvg is SVG doc
const g = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
$svg.appendChild(g);
this.paper = paper;
this.node = g;
this.attrs = {};
this._ = {};
this.add = function (el) {
g.appendChild(el.node);
};
};
GroupCollapse.prototype = Raphael.el;
Raphael.fn.groupCollapse = function(... args) {
return new GroupCollapse(this, ... args)
};
This is my realisation of custom group.
Then I create it like this:
let gc = paper.groupCollapse();
I make it 'hidden' And I have a function to make it visible that didn't work.
function showCollapsed() {
gc.visibility = "visible";
}
I want to have SVG group of Raphael circles that collapsing to one circle and unfolds on click to this circle. Could you help me and show the best way to do that. Thank you!

Related

How to make my cursor behave like a pen in my sketch webpage project?

I set up a grid 16x16 which a white background now i want the divs (grid items) to change color every time i press and hold my mouse and move over them, and to stop changing color when i stop the mousedown event, like in paint. in my script:
let gridContainer = document.getElementById('grid-container');
let gridArray = [];
for(let i = 0; i < 256; i++){
let div = document.createElement('div');
div.setAttribute('class', 'grid-item');
gridContainer.append(div);
gridArray[i] = div;
}
gridContainer.addEventListener("mousedown", draw)
gridContainer.addEventListener("mouseup",stopDraw)
function draw(){
gridArray.forEach(item => item.addEventListener("mouseover", () => {
(item.classList.add('hover'))
}));
}
function stopDraw(){
gridArray.forEach(item => item.removeEventListener("mouseover", () => {
(item.classList.add('hover'))
}));
}
The hover class is used to change the background color to blue.
I tried multiple other approaches but i always end up at the same place, the draw function not working unless i click which runs the function then it doesnt stop, it keeps running even after i leave the mouse.
Im using vanilla JS, I am still learning the basics.
Here is my code for better understanding my problem: https://codepen.io/ahmedmk11/pen/VwrmyGW
EDIT: I just realized that i didnt explain my problem in a clear way, so the draw function works properly, but my problem is that it doesnt stop working, i need in to only work when im pressing and holding only, like a pen.
Using event listeners is a good approach, to keep with this I have changed your code to use an object. Using an object allows the state of the mouse to be temporarily stored. Using the "canDraw()" method we can read from the object to determine if the mouse event is still occurring. It is still occurring if the key "mousedown" is still present.
The events now just add or remove the key from "mouseEvent" object.
let gridContainer = document.getElementById('grid-container');
let gridArray = [];
let mouseEvent = {};
for(let i = 0; i < 256; i++){
let div = document.createElement('div');
div.setAttribute('class', 'grid-item');
div.addEventListener("mouseover", draw);
gridContainer.append(div);
gridArray[i] = div;
}
gridContainer.addEventListener("mousedown", () => mouseEvent.mouseDown = true)
gridContainer.addEventListener("mouseup", () => delete mouseEvent.mouseDown)
function canDraw() {
return mouseEvent.mouseDown;
}
function draw(e){
if (canDraw()) {
e.fromElement.classList.add('hover');
}
}
Looks like you say that the div's should be paint when the mouse button is pressed, but you never really say that the divs should not be painted when the mouse button is not pressed, this happen in this specifc part:
function draw(){
gridArray.forEach(item => item.addEventListener("mouseover", () => {
(item.classList.add('hover'))
}));
}
function stopDraw(){
// right here!
gridArray.forEach(item => item.removeEventListener("mouseover", () => {
(item.classList.add('hover'))
}));
}
You could change your code to make something like "hey, when my mouse is up and over the divs, I don't want to insert the class that paint them", like this:
function draw(){
gridArray.forEach(item => item.addEventListener("mouseover", () => {
item.classList.add('hover');
}));
}
function stopDraw(){
// now we have a event that removes the class
gridArray.forEach(item => item.addEventListener("mouseover", () => {
item.classList.remove('hover');
}));
}
I also did some refactor (changed let by const when necessary, corrected the layout and moved the use of the functions draw and stopDraw after his declarations), I tried make less changes as I could, the final solution is:
const gridContainer = document.getElementById('grid-container');
let gridArray = [];
for(let i = 0; i < 256; i += 1){
const div = document.createElement('div');
div.setAttribute('class', 'grid-item');
gridContainer.append(div);
gridArray.push(div);
}
function draw(){
gridArray.forEach(item => item.addEventListener("mouseover", () => {
item.classList.add('hover');
}));
}
function stopDraw(){
gridArray.forEach(item => item.addEventListener("mouseover", () => {
item.classList.remove('hover');
}));
}
gridContainer.addEventListener("mousedown", draw);
gridContainer.addEventListener("mouseup", stopDraw);

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 increment and set an id for each node using javascript OOPS concept?

I am creating a Mind Mapping software using raphael.js and javascript.
I want to create a set of nodes where a node is like a rectangle and has its attributes as id, x,y coordinates etc. There is a plus icon on the rectangle on click of which a new node/rectangle is created with its new unique id. I want to create node and assign each created node its id in an Object Oriented fashion.
I am having difficulty in assigning a unique id to each node on its creation. I want to set ids starting from 0,1,2,3...
Second difficulty is selecting a node. I want to select a node based on its id.
Please have a look at the following code.
Could someone please help me in assigning ids to each of the nodes and to select each node based on its id?
assume that there is a canvas of 1000px width and 700px height.
paper = Raphael("canvas", 1000,700);
// create.js
drawNode(290, 80);
function drawNode(x,y)
{
id=0;
a = new node(x,y,id);
a.drawNode();
}
// nodes.js
var node = function(x,y,id){
this.id=id;
this.x=x;
this.y=y;
this.drawNode = function(){
var st = paper.set();
a = paper.rect(this.x,this.y, 100, 40, 2);
a.add = paper.image('images/plus.png', this.x+77, this.y+12, 20, 20)
a.attr({fill: '#8EDFF0', stroke: '#6AB0F2'});
st.push(a,a.text,a.add,a.addnote);
a.add.click(this.add);
}
this.add = function () {
id=1;
a = new node(this.attrs.x+150, this.attrs.y,id);
a.drawNode();
}
}
Please tell me how can I set unique id to each node instead of hardcoding the values and how to do that.
You, probably, need some NodeCostructor singleton with id parameter set to 0 from the start and createNode as function that accepts x and y, uses NodeCostructor's id to draw a node and returns you a node, than increases NodeCostructor's id value. A basic example of how should it be (EDIT: I've found some js singleton tutorial, so this new code will be more valid in terms of OOP):
var NodeConstructor = (function() {
var instance = null;
function NCInstanceCreate() {
var id=0;
createNode = function(x,y) {
tmp = new node(x, y, this.id);
this.id++;
return tmp;
}
return {
createNode : createNode,
id : id
}
}
function getInstance() {
if( ! instance ) {
instance = new NCInstanceCreate();
}
return instance;
}
return {
getInstance : getInstance
};
})(window);
var node = function(x,y,id){
this.id=id;
this.x=x;
this.y=y;
}
myConstructor=NodeConstructor.getInstance();
console.log(myConstructor.createNode(100,200));
console.log(myConstructor.createNode(110,220));
console.log(myConstructor.createNode(130,240));
console.log(myConstructor.id);
So you basically should use myConstructor.createNode(x,y) instead of new node(x,y,id) whenever you want to create node.

Creating a simple drawing area widget with google closure

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

Categories