Is there a way to prevent a Shape for being transformed? - vuejs3

That is the thing, i'm creating a shape that covers all canvas to act like a white background, i have a transformer and im listening to canvas clicks, inside it im adding objects into transformer nodes, but i don't like to transform the white shape that im using as a background.
Canvas white Shape

Just don't add transformer into it. I see several solutions:
Set listening = false on that background shape. In that case, it will not trigger any mouse/touch/pointer events
Or set a special name for it in just ignore in click callback
const background = new Konva.Rect({
fill: 'white',
width: stage.width(),
height: stage.height(),
name: 'nonSelectable'
});
stage.on('click', (e) => {
// ignore such shape
if (e.target.hasName('nonSelectable')) {
return;
}
// else attach transformer
});

Related

Changing the style of a leaflet cluster when pressing it

I have created a map with clusters created like so:
//create clustering markers
var markers = L.markerClusterGroup({
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: false,
singleMarkerMode: true, //makes sure that single incidents looks the same as clusters (but are still treated as single markers)
iconCreateFunction: defineClusterIcon
});
var layer_group = L.geoJSON(geoJson);
markers.addLayer(layer_group);
map.addLayer(markers);
map.fitBounds(markers.getBounds());
In the defineClusterIcon function, I create a SVG which then is converted to HTML and defines the icon:
return L.divIcon({
iconSize: new L.Point(40, 45),
html: html,
classname: 'leaflet-div-icon'
});
I now want to be able to change the style of the cluster (or marker, which also is styled as a cluster), when pressing it - and I want it to return to the original styling when pressed again.
Instead of changing the style of the actual svg elements, I am thinking that it might be easier to just change the style of the class:
.leaflet-div-icon {
background: rgba(0,0,0,0);
border: none;
}
Where I then want to have a border when the cluster/marker has been pressed. I do not know, whether it is possible to change the class within the on clusterclick or click functions, or if it can be done in another way.
My code, as it is now can be found here - where the wanted effect also can be seen on the controls on the right side: http://bl.ocks.org/skov94/f006cd45d2daa2bc67e4f514774fdd0d
Instead of switching the outline property of the leaflet-interactive div, i would toggle a class as you did with the controls on the right side (say a outlined class).
This class toggling has to be done in a "onclick" event handler. Leaflet clustering provide its own cluster click events (clusterclick).
The possible targets of the clusterclick event seem to be either the text, circle, or svg nodes of the cluster. We want to get the enclosing div with class leaflet-interactive to add or remove the outlined class on it. This will be made easily possible with Element.closest:
Javascript file
markers
[...]
.on('clusterclick',function(c) {
console.log("pressed");
map.closePopup();
c.originalEvent.target.closest(".leaflet-interactive")
.classList.toggle("outlined");
});
Then, simply change the style of its circle descendants with css:
CSS file
.leaflet-interactive.outlined circle {
stroke-width: 2px;
stroke: blue;
}
Edit: If you're not familiar with css, the selector means: circle nodes that are descendants of nodes with classes leaflet-interactive AND outlined.

Adding a color box for "Transparent" on the TinyMCE-4 color picker

I'd like an option with the TinyMCE color picker to choose transparent as the color so a character (a "bullet") will still be there and take up space but will not be visible if it's color is transparent.
There's an "X" box option that says "No color" but this seems to make the color black, not transparent.
Does anyone know how to add a transparent color option to this color picker, or even make the "X" box implement transparent instead of black?
Thanks for any ideas.
I believe I was able to do that, I did some quick tests and it appears to be working fine.
I got the latest version of TinyMCE (4.1.10_dev) to access the textcolor plugin's non minified javascript there's this instruction:
if (value == 'transparent') {
resetColor();
} else {
selectColor(value);
}
What happens here? When you choose a color it runs the selectColor, which wraps the selected text in a span with the selected color. However, when you select the no color it removes this color span (that's why it goes back to black which is the default color) instead of setting it to transparent.
So if you do this:
//if (value == 'transparent') {
// resetColor();
//} else {
selectColor(value);
//}
Instead of removing the span it will change it to 'transparent' instead.
One important thing is that tinyMCE gets the plugin scripts automatically, so it only works with the minified versions, so after you do these changes you'll have to minify the script to the plugin.min.js and put it on the textcolro plugin's folder overwriting the one there.
I hope it helps.
The × button in the colorpicker removes any custom colours, it does not add a zero-opacity colour.
As you can see when looking at the source code or trying the full example there is no support for rgba() or opacity in the included colorpicker plugin. Only rgb() and hex unfortunately.
You may need to create your own small plugin to add the ability. There are a number of alternatives, for example:
Create a CSS class which you can add to elements in the editor. Then do your colour magic in your own CSS file.
Create a new button in the toolbar which makes the element transparent.
I would personally go with option two, something like this:
tinymce.init({
selector: 'textarea',
plugins: 'nocolour',
toolbar: 'nocolour',
external_plugins: {
"nocolour": "url_to_your/nocolour.js"
}
});
And nocolour.js:
tinymce.PluginManager.add('nocolour', function(editor, url) {
// Add a button that opens a window
editor.addButton('nocolour', {
text: 'Remove Colour',
icon: false,
onclick: function() {
editor.undoManager.transact(function() {
editor.focus();
// Here is where you add code to remove the colour
editor.nodeChanged();
});
}
});
});
Rafael's solution worked for me. I just wanted to document it a bit more and show what it looks like for TinyMCE 4.1.7.
When you click the "X" in the textcolor grid the "value" variable gets "transparent," rather than a hex value from the colorMap.
The relevant code in the textcolor plugin is:
value = e.target.getAttribute('data-mce-color'); // the hex color from the colorMap square that was clicked. "transparent" if X was clicked
if (value) {
if (this.lastId) {
document.getElementById(this.lastId).setAttribute('aria-selected', false);
}
e.target.setAttribute('aria-selected', true);
this.lastId = e.target.id;
// if (value == 'transparent') { // occurs if you select the "X" square
// removeFormat(buttonCtrl.settings.format);
// buttonCtrl.hidePanel();
// return;
// }
selectColor(value);
The five lines I've commented out remove formatting for the selected text, leaving it black, which doesn't seem useful. If you wanted the text black you could select the black square in the colorMap. Falling through to selectColor(value) with value = "transparent" sets transparent as the color.

Place objects on a defined shape

I'm looking for a way, to allow a drag and drop, only on what's the area of an image.
So, if the image is the following:
I will have the option to add another layer on top, with some text, but that new layer, can't go anywhere, but inside the shape of that image.
So, after some research, I still wonder how can I achieve something like that?
I know I can use map and area to map existing elements on an image, but how can I add new elements, that fit only to that map? Any ideas?
To illustration your solution, let's assume you want to drop a raindrop in your cloud and be sure all raindrop pixels are fully inside the cloud...
Then the answer to your question requires asking this question:
Are all opaque raindrop pixels fully inside the cloud?
To answer this question, you must compare every pixel on the raindrop with every pixel underneath.
If the raindrop pixel is transparent, then ignore this pixel because this part of the raindrop is transparent anyway.
If the raindrop pixel is opaque and the pixel underneath is transparent, then this raindrop pixel is not contained in the cloud.
If both the raindrop pixel & underneath pixel are opaque, then this raindrop pixel is contained in the cloud.
You can get the required transparency information about the raindrop and cloud by drawing their images on a canvas and then requesting getImageData. 'getImageData' returns the red, green, blue & alpha information about every pixel on the canvas. To answer the question, we just need the alpha information.
Here's annotated code and a Demo:
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var offsetX,offsetY;
// load the cloud and raindrop images
var cloudmap,rainmap;
var rain=new Image();
rain.crossOrigin='anonymous';
rain.onload=start;
rain.src='https://dl.dropboxusercontent.com/u/139992952/raindrop1.png';
var cloud=new Image();
cloud.crossOrigin='anonymous';
cloud.onload=start;
cloud.src="https://dl.dropboxusercontent.com/u/139992952/cloud.png";
var cloud1=new Image();
cloud1.crossOrigin='anonymous';
cloud1.onload=start;
cloud1.src="https://dl.dropboxusercontent.com/u/139992952/multple/cloud1.png";
var imageCount=3;
function start(){
if(--imageCount>0){return;}
// resize the canvas to the size of the cloud
// and draw the cloud on the canvas
cw=canvas.width=cloud.width;
ch=canvas.height=cloud.height;
draw();
// create a transparency map of the cloud
cloudmap={
width:cloud.width,
height:cloud.height,
map:transparencyMap(cloud),
};
// create a transparency map of the raindrop
rainmap={
width:rain.width,
height:rain.height,
map:transparencyMap(rain),
}
// listen for mousemove events
$("#canvas").mousemove(function(e){handleMouseMove(e);});
// listen for window scroll events
calcCanvasOffset();
$(window).scroll(function(){ calcCanvasOffset(); });
}
function transparencyMap(img){
// create a temp canvas sized to the img size
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=img.width;
c.height=img.height;
// draw the img on the canvas
cctx.drawImage(img,0,0);
// get the pixel data for every pixel on the canvas
var data=cctx.getImageData(0,0,c.width,c.height).data;
// create an array that reports the status
// of every pixel on the canvas
// (status: true if opaque, false if transparent)
var map=[];
for(var i=0;i<data.length;i+=4){
map.push(data[i+3]>250);
}
return(map);
}
function draw(mouseX,mouseY,isContained){
// draw the cloud
ctx.clearRect(0,0,cw,ch);
if(isContained){
// draw the blue cloud indicating the raindrop is not fully contained
ctx.drawImage(cloud,0,0);
}else{
// draw the yellow cloud indicating the raindrop is fully contained
ctx.drawImage(cloud1,0,0);
}
// if the mouse position was supplied
if(mouseX){
ctx.drawImage(rain,mouseX-rain.width/2,mouseY-rain.height/2);
}
}
function AcontainsB(ax,ay,amap,bx,by,bmap){
// set a flag indicating of the raindrop is fully contained in the cloud
var isContained=true;
// calc the relative position of the raindrop vs cloud in the canvas
var deltaX=bx-ax;
var deltaY=by-ay;
// test every pixel of B against A
// if B is opaque and a is not opaque then B is not contained by A
var y=0;
while(isContained && y<bmap.height){
var x=0;
while(isContained && x<bmap.width){
// calc the map array indexes for the cloud(A) & raindrop(B)
var mapIndexA=(y+deltaY)*amap.width+(x+deltaX);
var mapIndexB=y*bmap.width+x;
// if the raindrop is opaque at this pixel
if(bmap.map[mapIndexB]){
// ...and if this pixel is off canvas
if(mapIndexA<0 || mapIndexA>=amap.map.length){
// ...then the raindrop is not in the cloud at this pixel
isContained=false;
// ...or if the pixel under the raindrop is transparent
}else if(!amap.map[mapIndexA]){
// ...then the raindrop is not in the cloud at this pixel
isContained=false;
}
}
x++;
}
y++;
}
return(isContained);
}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// calc the top-left corner of the raindrop image
var rainX=parseInt(mouseX-rain.width/2);
var rainY=parseInt(mouseY-rain.height/2);
// ask if the raindrop is fully contained in the cloud
var isContained=AcontainsB(0,0,cloudmap,rainX,rainY,rainmap);
// redraw the cloud & raindrop
draw(mouseX,mouseY,isContained);
}
// recalc the canvas offsetX & offsetY
function calcCanvasOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Use the mouse to drag the raindrop over the canvas<br>The cloud turns blue if the rain is fully inside the cloud</h4>
<canvas id="canvas" width=600 height=500></canvas>
You can use this "transparency mapping" to test anything you can draw into a canvas including images and text. Note that text is drawn with context.fillText.
If you're dropping a text element external to the canvas (maybe using jqueryUI or native "draggable"), you will have to:
Fetch the x,y position of the drop.
Fetch the text content of the dropped element.
Create a temporary canvas containing the text. Do it like this...
function textToCanvas(text,fontsize,fontface){
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
cctx.font=fontsize+'px '+fontface;
var textWidth=cctx.measureText(text).width;
c.width=textWidth;
c.height=fontsize+4;
cctx.font=fontsize+'px '+fontface;
cctx.textBaseline='top';
cctx.fillText(text,0,0);
return(c);
}
Use the temporary canvas just like an image to create a transparency map. This is possible because the canvas will accept another canvas as its image source.
Good luck with your project.
[ Additional questions from comments ]
Additional Question:
"How will this react when you have a raindrop placed somewhere in the
cloud (saved it's position), and then you try to add something that's
on top of it?" Said another way: "How can I test if 2 objects are
overlapping?"
Answer: You can test if 2 objects overlap by using transparency maps again. Create another test (AintersectsB) which tests if any pixel in A is opaque while the associated pixel in B is also opaque. You can start with the AcontainsB and modify it to create the AintersectsB test.
Additional Question:
"How can I save and later restore dropped object positions?"
Answer: Since canvas doesn't remember what it draws, you must remember for it. This is usually done by creating a javascript object for each dropped item and saving all these objects in an array. That way if you need to save the positions on a server you can use JSON.stringify to turn the array of objects into a string and send that string to the server to be saved in a database (or file). To recreate your work, the server pulls the string from the database and sends it to the browser. The browser uses JSON.parse to turn the string back into an array of javascript objects. Then you can redraw the scene exactly as it was using the information in the objects.

Affecting KineticJs.Image by css

Hello i'm working on a project that requires canvas manipulation. I need to draw an image and have to move it within the canvas. Which was not so hard to accomplish.. However i need to change my cursor into "move" when hovering the image like
img{
cursor:move;
}
I couldn't find any way to do this. Any suggestion??
Thanks in advance..
When you drag an Kinetic.Image, you get dragstart and dragend events.
You can change the cursor type in those event handlers:
// starting to drag -- display the move cursor
image1.on('dragstart', function () {
document.body.style.cursor = 'move';
});
// done dragging -- display the regular cursor
image1.on('dragend', function () {
document.body.style.cursor = 'default';
});
Basically you need an element which you can style on the page but with display: none, then put that element onto the canvas like this:
var image = document.getElementById('image');
context.drawImage(image, 0, 0);

Flex 4.5: custom background for popups

In Flex, when you use PopUpManager for popup windows, there is a background rectangle appearing over the application and behind the popup window itself. What I need is to override that default overlay rectangle with the custom one (in order to round the corners, apply gradient fill, etc).
How can this be achieved?
You can only change transparency, color and blur with css. See example:
global {
modalTransparency: 0.7;
modalTransparencyBlur: 0;
modalTransparencyColor: "0x000000";
}
Second way (if you want own design with round the corners, apply gradient fill, etc)
Create custom popup window (like TitleWindow) and when popup created or closed, dispatch from window custom event like:
dispatchEvent(new Event('addPopup', true));
In main application listen event:
systemManager.addEventListener("addPopup", onAddHandler, false, 0, true);
And then you can display own layer with custom design.
protected function onAddHandler(event:Event):void
{
// show custom background layer
}

Resources