for (el of chart.children) {
i++
previous__element = chart.children[i - 1]
if (el.classList.contains('rule')) {
//pass
} else {
line = el.children[0].children[0]
pos1 = previous__element.children[2].getBoundingClientRect()
position1 = {
top: pos1.top,
left: pos1.left,
}
pos2 = el.children[2].getBoundingClientRect()
console.log(previous__element.children[2])
console.log(el.children[2])
position2 = {
top: pos2.top,
left: pos2.left,
}
line.setAttribute('x1', Math.trunc(pos1.left))
line.setAttribute('y1', Math.trunc(pos1.top))
line.setAttribute('x2', Math.trunc(pos1.left))
line.setAttribute('y2', Math.trunc(pos1.top))
line.setAttribute('stroke', 'white')
}
}
html that gets output:
For some reason this does not actually show the lines, when hovering over them in dev tools it shows the height and width is 0. I'm trying to get the line to connect to the markers in the elements.
In this example I use position relative/absolute on all the elements. I don't know if that fits your solution, but the core of the example is that the SVG document is in the background of all the boxes. So, all the lines could be placed in that one SVG document.
Maybe the reason why your lines are not showing up is that they mis the stroke-width or that your SVG element does not have a width and a height.
let chart = document.querySelector('#chart');
let poschart = chart.getBoundingClientRect();
let line = chart.querySelector('svg line');
let boxs = chart.querySelectorAll('div.box');
let pos1 = boxs[0].getBoundingClientRect();
line.setAttribute('x1', pos1.x+pos1.width/2-poschart.x);
line.setAttribute('y1', pos1.y+pos1.height/2-poschart.y);
let pos2 = boxs[1].getBoundingClientRect();
line.setAttribute('x2', pos2.x+pos2.width/2-poschart.x);
line.setAttribute('y2', pos2.y+pos2.height/2-poschart.y);
#chart {
position: relative;
width: 400px;
height: 300px;
margin: 10px 20px;
border: thin solid black;
}
#chart svg {
position: absolute;
}
.box {
position: absolute;
padding: .5em;
border: thin solid black;
background-color: white;
}
<div id="chart">
<svg viewBox="0 0 400 300" width="100%" height="100%">
<line stroke="black" stroke-width="2"/>
</svg>
<div class="box" style="left:50px;top:180px">Box 1</div>
<div class="box" style="left:310px;top:100px">Box 2</div>
</div>
Related
Hellol,I have a code for a custom cursor, and the cursor, which is a ball/circle, was supposed to grow/scale when hovering over a link, if you see the code below, this function is there, but it is not working, does anyone know what's wrong? Thank you in advance. Note, I am unable to create a snippet here. The code is from codepen: https://codepen.io/clementGir/pen/RQqvQx
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<style>
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<script>
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('a');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
if (window.CP.shouldStopExecution(0)) break;
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
window.CP.exitedLoop(0); function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.clientX - 15,
y: e.clientY - 15
});
TweenMax.to($smallBall, .1, {
x: e.clientX - 5,
y: e.clientY - 7
});
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4
});
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1
});
}
</script>```
Growing cursor on hovering a link.
The codepen you are trying to copy links to external scripts and has been adapted to work inside codepen.
What I did to get the pen to work here (I think you only needed step 4):
For CSS & JS clicked down arrow top right, view compiled
Copy over all code
Settings>JS>External Scripts>Copy link into Stack Overflow snippet's external scripts
Removed 'window.CP' code junk which I assume is something to do with codepen and getting the window object of their sub-document
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('.hoverable');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.pageX - 15,
y: e.pageY - 15 });
TweenMax.to($smallBall, .1, {
x: e.pageX - 5,
y: e.pageY - 7 });
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4 });
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1 });
}
body {
height: 100vh;
background: #010101;
cursor: none;
margin: 0;
display: flex;
font-family: monospace;
}
body h1,
body p,
body a {
color: #fff;
}
body a {
border-bottom: 2px solid #fff;
padding: 10px 0;
margin-top: 25px;
}
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
body .left,
body .right {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
body .right {
background: #fff;
}
body .right a {
border-bottom: 2px solid #000;
}
body .right h1,
body .right p,
body .right a {
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<div class="left">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>
<div class="right">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>
I'm trying to offset the element containing the image which has 21 frames. 0 - 21. I've placed 21 vertical columns over the image to visualize which frame should be present when the user's cursor is within the column lines. So each time your cursor moves into a different column of the grid, it should display a new frame. I need help figuring out whey the last frame (20) only shows when the user's cursor is on the very last pixel to the far right of the frame?
All the work is done in the javascript. I've commented each step and print to the console useful information regarding the math.
https://jsfiddle.net/JokerMartini/2e9awc4u/67/
window.onload = function() {
console.log('go')
$("#viewport").mousemove(function(e) {
// step 0: value to offset each frame (without scale)
const frameWidth = 320
// step 1: get the current mouse position in relation to the current element
const x = e.offsetX
// step 3: get width of viewable content, subtract 1 pixel starts at 0px
const viewWidth = $("#viewport").width() - 1
// step 4: find the % of the current position (in decimals 0-1.0)
const percent = x / viewWidth
// step 5: find the frame by the current percentage
const filmstripWidth = $("#filmstrip").width()
const frameByPercent = Math.round((filmstripWidth - frameWidth) * percent)
// step 6: find the nearest multiplier to frameWidth to offset
const offset = Math.floor(frameByPercent / frameWidth) * frameWidth
// const offset = -frameByPercent // smooth
// step 7: set that as the current position in negative (for offset reasons)
$("#filmstrip").css('transform', 'translate(' + -offset + 'px)')
console.log(
'CURSOR:', x,
'VIEW:', viewWidth,
'PERCENT:', percent,
'IMAGE WIDTH:', filmstripWidth,
frameByPercent
)
});
};
html {
height: 100%;
width: 100%;
}
#filmstrip {
will-change: transform;
pointer-events:none;
}
#margin-center {
background: grey;
padding: 30px
}
#viewport {
height: 180px;
width: 320px;
background: #FFFFAA;
display: block;
margin: auto;
position: relative;
overflow: hidden; /* Comment for debugging */
}
#guides {
position: absolute;
top: 0;
left: 0;
pointer-events:none;
}
#content {
display: inline-block;
font-size: 0;
height: auto;
max-width: 400px;
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
<div id="margin-center">
<div id='viewport'>
<img id='filmstrip' src="https://i.ibb.co/7XDpcnd/timer.jpg" width="auto" height="180">
<svg id="guides" width="320px" height="180px">
<defs>
<pattern id="grid" width="15.238" height="180" patternUnits="userSpaceOnUse">
<path d="M 16 0 L 0 0 0 180" fill="none" stroke="black" stroke-width="1" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
</div>
</div>
</div>
Your results are offset by 1 because you deducted one full frameWidth.
Added code to cap percent at 0.999, to prevent it jumping to 22nd frame. mousemove positions will sometimes be at end position or greater.
window.onload = function() {
console.log('go')
$("#viewport").mousemove(function(e) {
// step 0: value to offset each frame (without scale)
const frameWidth = 320
// step 1: get the current mouse position in relation to the current element
const x = e.offsetX
// step 3: get width of viewable content, subtract 1 pixel starts at 0px
const viewWidth = $("#viewport").width() - 1
// step 4: find the % of the current position (in decimals 0-1.0)
const percent = x / viewWidth
// step 5: find the frame by the current percentage
const filmstripWidth = $("#filmstrip").width()
const frameByPercent = Math.round((filmstripWidth) * Math.min(percent,0.999))
// step 6: find the nearest multiplier to frameWidth to offset
const offset = Math.floor(frameByPercent / frameWidth) * frameWidth
// const offset = -frameByPercent // smooth
// step 7: set that as the current position in negative (for offset reasons)
$("#filmstrip").css('transform', 'translate(' + -offset + 'px)')
console.log(
'CURSOR:', x,
'VIEW:', viewWidth,
'PERCENT:', percent,
'IMAGE WIDTH:', filmstripWidth,
frameByPercent
)
});
};
html {
height: 100%;
width: 100%;
}
#filmstrip {
will-change: transform;
pointer-events:none;
}
#margin-center {
background: grey;
padding: 30px
}
#viewport {
height: 180px;
width: 320px;
background: #FFFFAA;
display: block;
margin: auto;
position: relative;
overflow: hidden; /* Comment for debugging */
}
#guides {
position: absolute;
top: 0;
left: 0;
pointer-events:none;
}
#content {
display: inline-block;
font-size: 0;
height: auto;
max-width: 400px;
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
<div id="margin-center">
<div id='viewport'>
<img id='filmstrip' src="https://i.ibb.co/7XDpcnd/timer.jpg" width="auto" height="180">
<svg id="guides" width="320px" height="180px">
<defs>
<pattern id="grid" width="15.238" height="180" patternUnits="userSpaceOnUse">
<path d="M 16 0 L 0 0 0 180" fill="none" stroke="black" stroke-width="1" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
</div>
</div>
</div>
Please run the snippet and drag you mouse over the bar to make it red.
If you drag the mouse very slowly, you will fill it red, but if you move it fast, there will be white holes in it.
How to fix it? (the white holes)
I want to make a bar divided into 500 parts and if you hover it, it becomes red and being able to drag fast and fill it without holes.
Any help appreciated :)
$(function() {
var line = $("#line");
for ( var i = 0; i < 500; i++) {
line.append('<div class="tile" id="t'+(i+1)+'"></div>');
}
var tile = $(".tile");
tile.hover (
function() { //hover-in
$(this).css("background-color","red");
},
function() { //hover-out
}
);
});
#line{
height: 50px;
background-color: #000;
width: 500px;
}
.tile {
height: 50px;
float: left;
background-color: #ddd;
width: 1px;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<div id="line"></div>
With your design one way would be to iterate over the first to your current hovered element and fill it, which would lead no spaces. That said you may want to consider using the HTML5 Canvas and drawing a rectangle from 0 to your mouse position, which will perform significantly faster.
$(function() {
var line = $("#line");
for ( var i = 0; i < 500; i++) {
line.append('<div class="tile" id="t'+(i+1)+'"></div>');
}
var tile = $(".tile");
tile.hover (
function() { //hover-in
var self = this;
$("#line").children().each(function(){
$(this).css("background-color","red");
if(this == self) return false;
});
},
function() { //hover-out
}
);
});
#line{
height: 50px;
background-color: #000;
width: 500px;
}
.tile {
height: 50px;
float: left;
background-color: #ddd;
width: 1px;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<div id="line"></div>
Edit
Below is an example doing the same task but using the HTML 5 Canvas:
$("#line").mousemove(function(e){
var canvas = $(this)[0];
var ctx = canvas.getContext("2d");
var rect = canvas.getBoundingClientRect()
var x = e.clientX - rect.left;
ctx.fillStyle="red";
ctx.fillRect(0, 0, x, canvas.height);
});
#line{ background-color: #ddd; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="line" width=500 height=50 ></canvas>
This is another approach with nextUntil to select siblings..
$(function() {
var line = $("#line");
for ( var i = 0; i < 500; i++) {
line.append('<div class="tile" id="t'+(i+1)+'"></div>');
}
var tile = $(".tile");
line.on( 'mouseover', function(ev){
$('.tile').first().nextUntil( $('.tile').eq(ev.pageX) ).css("background-color","red");
});
line.on( 'mouseleave', function(ev){
$('.tile').css("background-color","#ddd");
});
});
#line{
height: 50px;
background-color: #000;
width: 500px;
}
.tile {
height: 50px;
float: left;
background-color: #ddd;
width: 1px;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<div id="line"></div>
Another solution makes use of jQuery's mousemove method. This allows the bar to go both forward and backwards, simply following the cursors position.
This detects movement inside of the div, then I calculate the position of the cursor within the div as a percentage and apply it as the width of the red bar.
$( ".bar" ).mousemove(function( event ) {
var xCord = event.pageX;
xPercent = (xCord + $('.pct').width()) / $( document ).width() * 100;
$('.pct').width(xPercent+'%');
});
.bar{
background:'#999999';
width:50%;
height:50px;
}
.pct{
height:100%;
background:red;
width:0%;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js">
</script>
<div class="bar" style="background:#999999">
<div class="pct"></div>
</div>
I have been trying to get the blur effect to work when creating the elements and imbedding them with JavaScript. I cannot figure out why the blur will not show up. I have two fiddles, one with the standard HTML & CSS and the other using JavaScript. If anyone wants to check them out and offer an explanation as to what I am doing wrong I would really appreciate it.
HTML & CSS Version (Works)
https://jsfiddle.net/aaronbalthaser/xampLtnr/
HTML:
<div id="container">
<img src="http://lorempixel.com/300/250/sports/" width="300" height="250" />
<svg width="300" height="60" viewbox="0 0 300 60">
<defs>
<filter id="blur">
<fegaussianblur in="SourceGraphic" stddeviation="5" />
</filter>
</defs>
<image filter="url(#blur)" xlink:href="http://lorempixel.com/300/250/sports/" x="0" y="-190" height="250px" width="300px" />
</svg>
</div>
CSS:
#container {
position: relative;
width: 300px;
height: 250px;
margin: 0 auto;
position: relative;
}
svg {
position: absolute;
bottom: 0;
left: 0;
}
JavaScript Version (Doesn't Work)
https://jsfiddle.net/aaronbalthaser/Lbnkfn02/
HTML:
<div id="container">
<img src="http://lorempixel.com/300/250/sports/" width="300" height="250" />
</div>
CSS:
#container {
position: relative;
width: 300px;
height: 250px;
margin: 0 auto;
position: relative;
}
svg {
position: absolute;
bottom: 0;
left: 0;
}
JS:
var width = 300;
var height = 250;
var viewbox = "0 0 300 60";
var namespace = 'http://www.w3.org/2000/svg'
var path = 'http://lorempixel.com/300/250/sports/';
var container = document.getElementById('container');
var body = document.body;
var svg = document.createElementNS(namespace,'svg');
svg.setAttribute('width', width);
svg.setAttribute('height', 60);
svg.setAttribute('viewBox', viewbox);
var defs = document.createElementNS(namespace, 'defs');
var filter = document.createElementNS(namespace, 'filter');
filter.setAttribute('id', 'blur');
var feGaussianBlur = document.createElementNS(namespace, 'feGaussianBlur');
feGaussianBlur.setAttribute('in', 'SourceGraphic');
feGaussianBlur.setAttribute('stdDeviation', '5');
var image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
image.setAttribute('filter', 'url(#blur)');
image.setAttribute('xlink:href', path);
image.setAttribute('x', + '0');
image.setAttribute('y', + '-190');
image.setAttribute('height', height + 'px');
image.setAttribute('width', width + 'px');
filter.appendChild(feGaussianBlur);
defs.appendChild(filter);
svg.appendChild(defs);
svg.appendChild(image);
container.appendChild(svg);
console.log(container);
image.setAttribute('xlink:href', path);
should be
image.setAttributeNS("http://www.w3.org/1999/xlink", 'xlink:href', path);
https://jsfiddle.net/Lbnkfn02/2/
I have this HTML that renders a simple arrow sign pointing towards the right:
<!DOCTYPE html>
<html>
<head>
<style>
div { width: 0px; height: 0px; border-left: 20px solid black; border-top: 20px solid transparent; border-bottom: 20px solid transparent; border-right: 20px solid transparent; position: absolute; left: 35px; top: 53px; cursor: pointer; }
</style>
<body>
<div></div>
</body>
</html>
If you hover of it, the cursor turns to pointer. But because it is actually a square div, the cursor turns pointer even if you are just outside the arrow within the perimeter of the div.
So I wrote this Javascript addition such that the cursor turns pointer only when the mouse is hovering over that arrow. For this purpose, I figured the coordinates of the three vertices of the triangle from Firebug ((35,53),(55,73),(35,93) clockwise from top). Then I check whether the point in question lies inside the triangle formed by these 3 vertices. This I do by checking whether the point and the opposite vertex for each edge lies on the same side of that edge or not (if they do, the product of the values obtained by substituting the coordinates of that point for x and y in that equation will be positive).
<!DOCTYPE html>
<html>
<head>
<style>
div { width: 0px; height: 0px; border-left: 20px solid black; border-top: 20px solid transparent; border-bottom: 20px solid transparent; border-right: 20px solid transparent; position: absolute; left: 35px; top: 53px; }
.hoverclass { cursor: pointer; }
</style>
<script src="jquery.js">
</script>
<script>
$(document).ready(function(){
$("div").click(function(e) { alert(e.pageX + " " + e.pageY); });
function l1(x,y) { return y - x - 18; }
function l2(x,y) { return x+y-128; }
function l3(x,y) { return x-35; }
$("div").hover(function(e) {
var x = e.pageX;
var y = e.pageY;
if (l1(x,y)*l1(35,93) >= 0 && l1(x,y)*l1(35,93) >= 0 && l1(x,y)*l1(35,93) >= 0 ) {
$(this).addClass('hoverclass');
}
else { $(this).removeClass('hoverclass'); }
},
function() {
$(this).removeClass('hoverclass');
});
});
</script>
<body>
<div></div>
</body>
</html>
However, the results are not predictable. Sometimes the cursor turns pointer within the triangle only, sometimes outside as well (just as before), and sometimes not at all. I suspect that this is probably due to the hover function working overtime, that may be temporarily hanging the script. Is there any other way to achieve this?
This could be done using HTML5 canvas. Basic idea is to check for pixel color on mousemove on canvas element. This way, your element can be of any form as you wish. Of course, you should make some optimization of following code:
SEE WORKING DEMO
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
// set up triangle
var example = document.getElementById('example');
var context = example.getContext('2d');
context.fillStyle = '#000';
context.strokeStyle = '#f00';
context.lineWidth = 1;
context.beginPath();
// Start from the top-left point.
context.moveTo(10, 10); // give the (x,y) coordinates
context.lineTo(60, 60);
context.lineTo(10, 120);
context.lineTo(10, 10);
// Done! Now fill the shape, and draw the stroke.
// Note: your shape will not be visible until you call any of the two methods.
context.fill();
context.stroke();
context.closePath();
$('#example').mousemove(function(e) {
var pos = findPos(this);
var x = e.pageX - pos.x;
var y = e.pageY - pos.y;
var coord = "x=" + x + ", y=" + y;
var c = this.getContext('2d');
var p = c.getImageData(x, y, 1, 1).data;
if(p[3]!='0') $(this).css({cursor:'pointer'});
else $(this).css({cursor:'default'});
});
You'd better use CSS instead. With :before and :after pseudo classes you can do magic. Check out this Pure CSS GUI icons by Nicolas Gallagher.
If you use any CSS pre-processor, these icons can be wrapped up as a mixin, this way required properties can be assigned like this:
#icon > .close(16px, #fff, #E83921);
You can make any shape have cursor pointer with CSS only. The idea is to rotate wrapper container which has overflow: hidden (you can have several of them depending on the shape you need). In case of OP problem this code does a trick:
<div class="arrow"><i></i></div>
.arrow {
margin: 100px;
border_: 1px red solid;
width: 40px;
height: 40px;
overflow: hidden;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.arrow i {
height: 65px;
width: 65px;
background-color: green;
content: '';
display: block;
cursor: pointer;
margin: -35px 0 0 11px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
See this demo: http://cssdesk.com/PaB5n
True that this requires CSS transform support so it's not cross browser.