Change opacity of an area based on mouse position - javascript

Is it possible to change the opacity of background but only underneath the cursor area (for example a white small circle)? I am thinking of it a bit like a basic heatmap but the heat doesn't stay - it just follows the cursor.
At the moment I have the following
HTML:
html {
background-color: #000;
width: 100%;
height: 100%;
}
JS:
$(document).mousemove(function(event){
var i = event.pageX.toPrecision(1) / 1000;
$("html").css('opacity', i)
});
Sorry this is probably a very basic starting point. Would I need to use canvas?

You can do that using svg
What i did :-
I placed two same images with same co ordinates,height and width and gave a circular clip-path to the one on top (which has full opacity) when mouse moves the position of the circle also changes
$('svg').on('mousemove',function(e){
$('.a').attr('cx',e.pageX).attr('cy',e.pageY)
})
.one{
opacity:.5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="500" height="500">
<clippath id="clip" >
<circle cx="50" cy="50" r="50" class="a"/>
</clippath>
<image xlink:href="https://images.unsplash.com/photo-1474575981580-1ec7944df3b2?dpr=1&auto=format&fit=crop&w=1500&h=934&q=80&cs=tinysrgb&crop=&bg=" width="500" height="500" x="0" y="0" class="one"/>
<image xlink:href="https://images.unsplash.com/photo-1474575981580-1ec7944df3b2?dpr=1&auto=format&fit=crop&w=1500&h=934&q=80&cs=tinysrgb&crop=&bg=" width="500" height="500" x="0" y="0" clip-path="url(#clip)"/>
</svg>

Related

Click only through holes in svg mask

I have svg mask which determines holes in rectangular. Behind svg mask I have some clickable elements and I would like to pass events to them, but only through holes. I've experimented with pointer-events values, but I can only make either whole mask to pass events or whole mask to capture them. For one hole it can be simply done using clip-path, just determining outer part of the hole, but several holes make things more difficult. Is there any possibility to avoid using clip-path? I also tried pointer-events: visiblePainted and pointer-events: painted, but had no success.
.background {
width: 400px;
height: 400px;
background: red;
cursor: pointer;
}
.svg {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
<button class="background">
</button>
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg" class="svg">
<defs>
<mask id="mask">
<rect
x="0"
y="0"
width="400"
height="400"
fill="white"
/>
<rect
x="20"
y="20"
width="40"
height="40"
fill="black"
/>
<rect
x="290"
y="290"
width="40"
height="40"
fill="black"
/>
</mask>
</defs>
<rect
x="0"
y="0"
width="400"
height="400"
fill="black"
opacity="0.5"
mask="url(#mask)"
pointer-events="auto"
/>
</svg>
There are several aspects to this problem. First, you are right the behavior of masks and clip-paths is different in relation to hit-testing.
A clip path is a geometric boundary, and a given point is clearly either inside or outside that boundary; thus, pointer events must be captured normally over the rendered areas of a clipped element, but must not be captured over the clipped areas... By contrast, a mask is not a binary transition, but a pixel operation, and different behavior for fully transparent and almost-but-not-fully-transparent may be confusingly arbitrary; as a consequence, for elements with a mask applied, pointer events must still be captured even in areas where the mask goes to zero opacity.
Second, a clip-path is a geometric shape, but just like all paths, it might contain holes. Instead of three <rect>s, you can use one <path> with three subpaths, as long as the clip-rule makes sure the subpaths inside get cut out of the surrounding shape.
Third, if the pointer-events property is applied to an <svg> element in a HTML context, its behavior becomes...strange. Any other value than pointer-events: none on the <svg> element lead to the whole bounding box receiving events - a behavior proposed for HTML elements, but currently not part of any spec.
The solution here is to set pointer-events: none on the <svg> element, and then to reverse that with pointer-events: painted on the child <rect> element.
button, svg {
position:absolute;
width:400px;
height:400px
}
button {
background: #0000ff;
cursor: pointer;
}
button:hover {
background: #008800;
}
svg {
pointer-events: none;
}
.over {
fill: #000;
clip-path: url(#clip);
pointer-events: painted;
}
<button></button>
<svg xmlns="http://www.w3.org/2000/svg" height="400" width="400">
<defs>
<clipPath id="clip" clip-rule="evenodd">
<path d="M 20 20 h 360 v 360 h -360 z
M 40 40 v 40 h 40 v -40 z
M 200 290 v 40 h 40 v -40 z" />
</clipPath>
</defs>
<rect y="0" x="0" height="400" width="400" class="over" />
</svg>
Clip masks are useful for cropping parts out of complicated objects, but if you're just working with blocks of solid colour then maybe it would be just as easy to create shapes that already have holes in them.
I've added an example below. Does this help?
<svg width="400" heoght="200" viewBox="0 0 400 200">
<text x="100" y="100" text-anchor="middle"
alignment-baseline="middle" onclick="alert('Hello!')"
style="cursor:pointer">Click me</text>
<text x="300" y="100" text-anchor="middle"
alignment-baseline="middle" onclick="alert('Hello?')"
style="cursor:pointer">Not me</text>
<path d="M20 20 180 20 180 180 20 180ZM60 60 60 140 140
140 140 60Z" fill="#3a6" fill-opacity="0.7"
fill-rule="nonzero"/>
<path d="M220 20 380 20 380 180 220 180Z" fill="#f20"
fill-opacity="0.7"/>
</svg>

Difference between canvas and SVG (fill effect)

I have a basic question about canvas and SVG. I want to create an overlay with holes and fill it with some color.
It seems to work using canvas, but I want to try SVG (to handle event e.g. resize).
canvas.width = 200;
canvas.height = 200;
var context = canvas.getContext('2d');
//fill background
context.globalAlpha = 0.8;
context.fillStyle = "blue"
context.fillRect(0, 0, 200, 200);
context.globalCompositeOperation = 'xor';
context.globalAlpha = 1.0;
context.fillStyle = "rgba(0,0,0,1)";
context.fillRect(50, 50, 50, 50);
https://jsfiddle.net/gpx21/195ygzhq/
but SVG mask looks too light.
<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="
position: absolute;
z-index: 19000;
top: 0;
left: 0;
">
<defs>
<mask id="mask1" maskContentUnits="userSpaceOnUse">
<rect x="0" y="0" width="200" height="200" style="opacity: 0.8; fill:blue;"></rect>
<path d="M50 50 H100 V100 H50Z" id="hole"></path>
</mask>
</defs>
<rect width="200" height="200" style="fill:blue; mix-blend-mode: darken;mask:url(#mask1);"></rect>
</svg>
https://jsfiddle.net/gpx21/1fktpnr5/
how can I archieve the same effect as in canvas? Thanks.
You can think of an SVG mask as a grayscale image. Where that image is white, the masked element is visible, and where it is black, the masked element is transparent.
So, to have a translucent blue <rect>, you can apply a <mask> that is mostly light gray (i.e. "almost white") except for a black part which will result in a transparent hole:
<defs>
<mask id="mask1" maskContentUnits="userSpaceOnUse">
<rect x="0" y="0" width="200" height="200" fill="lightgray"></rect>
<path d="M50 50 H100 V100 H50Z" id="hole" fill="black"></path>
</mask>
</defs>
<rect width="200" height="200" style="fill:blue; mask:url('#mask1');"></rect>
https://jsfiddle.net/1fktpnr5/1/
This is a simple fix. When you define masks in your svg documents, mask elements can be given a fill of black to hide them, a fill of white to display them, or something in between for varying levels of transparency. (source)
In your mask code:
<defs>
<mask id="mask1" maskContentUnits="userSpaceOnUse">
<rect x="0" y="0" width="200" height="200" style="opacity: 0.8; fill:blue;"></rect>
<path d="M50 50 H100 V100 H50Z" id="hole"></path>
</mask>
</defs>
You can change your fill:blue to fill:white and this should fix it.

Making inline SVG responsive in browsers without using javascript?

I've been reviewing articles like this one which are really good, but possibly dated, and I'm wondering if any progress has been made on a simple way to make inline svg responsive while maintaining the aspect ratio?
In other words if we have svg with a 1:1 aspect ratio inside a div container, and the container shrinks from 400x400px to 200x200 px, then the view box width and height parameters double.
I am confused about what you want. I don't know what you mean, in your question, when you talk about "view box width and height parameters double". Perhaps you are confused about how viewBox works.
As long as the SVG has a viewBox it should be responsive.
In the following example, I've put the SVG in a div container and animated the container size to simulate the page size changing.
#container {
width: 200px;
background: linen;
animation: scale 1s alternate infinite;
}
#keyframes scale {
from { width: 200px; }
to { width: 100px; }
}
<div id="container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="gold"/>
<rect x="10" y="50" width="20" height="50" fill="gold"/>
<rect x="70" y="50" width="20" height="50" fill="gold"/>
<circle cx="35" cy="45" r="5"/>
<circle cx="65" cy="45" r="5"/>
</svg>
</div>
But it could just as easily be a container that is the width of the page. Try running the following snippet. Then click "Fullpage" and resize the browser window.:
#container {
width: 100%;
background: linen;
}
<div id="container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="gold"/>
<rect x="10" y="50" width="20" height="50" fill="gold"/>
<rect x="70" y="50" width="20" height="50" fill="gold"/>
<circle cx="35" cy="45" r="5"/>
<circle cx="65" cy="45" r="5"/>
</svg>
</div>
If you don't need to change svg with css or modify it, I suggest you to use it as an img element. You create an img and put the path to your svg in the src. Here's what I mean: https://codepen.io/tlemaitre/pen/PBKGOo
I've recently had a similar problem, I have a container that needs to be preserveAspectRatio="none" to keep the shape fluid on different window sizes but the image inside gets gets distorted. I've not seen any solution to this problem with or without JS, so I don't think it's possible.

Am I handling SVG's correctly?

I have a square div with two triangles drawn via SVGs within that looks like this :
I then have a button that once the onClick event is triggered it will add a class called hidden just basically has : display: none; visibility: hidden; to hide the bottom triangle, then displays two more triangles like so :
My current code looks like :
SVG to draw my triangle.
<svg id="triangle" width="100%" height="100%">
<path d="M0,0 L680,0 L0,680 Z " id="top_triangle" class="o-top_main"></path>
</svg>
Then the image for me to be able to use a background image :
<svg style="position: absolute;">
<defs>
<pattern id="image_top" width="1" height="1" viewBox="0 0 100 100">
<image id="svg_img_top" xlink:href='http://41.media.tumblr.com/tumblr_m8r9k312PE1qc6mk8o1_500.jpg' width="100" height="100" preserveAspectRatio="none"></image>
</pattern>
</defs>
</svg>
I've been drawing the SVG path like such <path d="M0,0 L680,0 L0,680 Z ". I've tried drawing an SVG triangle in photoshop, exporting out as an SVG then getting the path in the code and pasting in but the dimensions are never correct so I end up editing the path to the point where i've redrawn it anyway.
Is there anyway way I can draw an SVG triangle in Photoshop where I can then paste the path into my site without needing to change the path to the point where i've redrawn it?
My end site kind of looks similar to this :
Lets take a look at your first image here:
Now usually you allways want to create an viewBox on any svg element.
Width and height are usually set in css, but there are lots of cases where setting it in inline is better.
Now lets see your drawing 2 triangles with paths so and then adding another:
My suggestion is drawing 3 triangles then changing the color:
Update:
now with image url instead of colors. (the svg is getting quite complicated now)
var bottom = document.getElementById("tri_tri");
bottom.addEventListener("click", function() {
this.style.fill = "yellow";
this.style.stroke = "yellow";
});
/**/
.triangleArt {
width: 250px;
}
#tri_one {
fill: firebrick;
}
#tri_two {
fill: url(#image_top);
stroke: url(#image_top);
}
#tri_tri {
fill: url(#image_top);
stroke: url(#image_top);
cursor: pointer;
}
<svg viewBox="0 0 100 100" class="triangleArt" xmlns="http://www.w3.org/2000/svg" xmlns:x="http://www.w3.org/1999/xlink">
<defs>
<pattern id="image_top" patternUnits="userSpaceOnUse" width="250" height="156">
<image id="svg_img_top" xlink:href='http://41.media.tumblr.com/tumblr_m8r9k312PE1qc6mk8o1_500.jpg' x="0" y="0" width="250" height="156"></image>
</pattern>
</defs>
<path id="tri_one" d="m0,100 100,-100, -100,0z" />
<path id="tri_two" d="m100,0 0,100 -50,-50z" />
<path id="tri_tri" d="m100,100 -50,-50 -50,50z" />
</svg>

SVG view box crop using jquery

I am doing sticker website for my client. They need image crop within given SVG image. That SVG image is unshaped like Halloween face (see below).
When i upload an image, it will display within the given image. The rest of the image will hide.
How can i achieve this?
This is really quite easy with SVG clipPaths. It has good browser support as well, except of course IE.
.defs {
position: absolute;
width: 0;
height: 0;
}
.demo {
text-align:center;
}
.star image {
clip-path: url(#star-clip);
}
.hex image {
clip-path: url(#hex-clip);
}
.heart image {
clip-path: url(#heart-clip);
}
.shape image {
clip-path: url(#shape-clip);
}
<svg class="defs">
<defs>
<clipPath id="star-clip">
<polygon id="star" points="150,7.3 196.4,101.3 300,116.3 225,189.4 242.7,292.7 150,243.9 57.3,292.7 75,189.4 0,116.3 103.6,101.3" />
</clipPath>
<clipPath id="hex-clip">
<polygon id="hex" points="222.5,295 77.5,295 5,150 77.5,5 222.5,5 295,150" />
</clipPath>
<clipPath id="heart-clip">
<path id="heart" d="M248.078,5.883c-36.691-14.739-77.771-0.839-98.517,31.125C128.817,5.044,87.735-8.856,51.043,5.883 C9.354,22.632-10.863,70.009,5.887,111.696c16.06,39.98,143.314,139.607,143.314,139.607l0.359,0.28l0.36-0.28 c0,0,127.251-99.627,143.314-139.607C309.986,70.009,289.768,22.632,248.078,5.883z"/>
</clipPath>
<clipPath id="shape-clip">
<path id="shape" d="M132.9,0.9c-12.5,1-34.7,4.4-44.9,6.7c-33.5,7.6-55.7,28.2-63.8,58.8c-1.8,6.9-2.2,9.7-2.3,20.1c0,10.5,0.2,13.3,2.3,21.6 c9.5,40.7,33.2,90.2,71.5,149.8c10.5,16.4,14.8,21.8,22.8,29.3c4.3,3.9,8.3,6.8,13,9.2l6.9,3.4h10.9c10.9,0,10.9,0,17-2.9 c7.6-3.7,14.3-8.7,21.4-16.2c6.9-7.2,10.5-12.3,21.9-30.3C248,188.7,272,134.3,277.4,96.6c1.2-8.6,0.5-22.4-1.7-31.3 c-5.6-23.4-20.8-41.2-43.6-50.9c-15.3-6.5-27.5-10-44.5-12.5C177.7,0.5,144.9-0.1,132.9,0.9z"/>
</clipPath>
</defs>
</svg>
<div class="demo heart">
<svg width="300" height="300">
<image xlink:href="http://lorempixel.com/output/city-q-c-300-300-4.jpg" width="300" height="300" />
</svg>
</div>
<div class="demo star">
<svg width="300" height="300">
<image xlink:href="http://lorempixel.com/output/cats-q-c-300-300-10.jpg" width="300" height="300" />
</svg>
</div>
<div class="demo hex">
<svg width="300" height="300">
<image xlink:href="http://lorempixel.com/output/people-q-c-300-300-8.jpg" width="300" height="300" />
</svg>
</div>
<div class="demo shape">
<svg width="300" height="300">
<image xlink:href="http://lorempixel.com/output/nightlife-q-c-300-300-6.jpg" width="300" height="300" />
</svg>
</div>
You'll need to define your clipPath and then apply it to your image. In the demo, I created a separate SVG element that contains all of the shapes as path or polygon definitions and simply applied them using external styles. You can of course do this in a single SVG, such as:
<svg width="120" height="120">
<defs>
<clipPath id="shape-clip">
<path id="shape" d="M60,117.4c-20.4,0-44.8-53.6-49-76.4C7.3,21,26.6,4.2,46.9,2.6c7-0.5,19.8-0.2,26.2,0 C93.5,3.4,112.7,21,109,41C104.8,63.8,79.3,117.4,60,117.4z"/>
</clipPath>
</defs>
<image xlink:href="http://lorempixel.com/output/nightlife-q-c-120-120-6.jpg" width="120" height="120" style="clip-path: url(#shape-clip);" />
</svg>
Some things to keep in mind:
Your defs need to be defined in the source order before they are used (so you can't put them at the end of your page for example).
The SVG element defs will not display, but they will take up space in the rendered page. I just force them out of the document flow with position: absolute and give them a height and width of 0 to make sure they don't mess with my layout.
For more information, you might want to look at these:
Clipping and Masking in CSS, CSS Tricks
SVG Clip Path Tutorial, Jenkov.com
Clipping, Masking and Compositing Specification, W3C

Categories