I'm making a progressive line bar using SVG elements, my logic is that for every number of inputs , the line gets divided on even parts and each part will get colored by percentage, depending of the number of valid interactions.
In the console I can see the logic is working as it should, however the coloring of the line is not.
I'm not fully sure how can I make that the progress gets colored from left to right progressively, right now its behaviour doesn't follow the desired path.
function updateBar() {
//Path that will be painted
var myProgress = document.getElementById("myProgress");
//Reference the number of inputs
var numberOfInputs =document.getElementById("totalInput").value;
//number that we will use to divide the valid interaction with the inputs
var interactionTimes = document.getElementById("validInput").value;
//getting the # to get the right percent to paint the line
var lineDivision = [(100 / numberOfInputs)];
console.log("this is lineDivision:" + lineDivision);
//and then with this lineDivision variable, replace the 20 from the previous code. But here is where I have the main problem, because as result of every click I do in a checkbox the var percent always get 100 and as result the ring is already fully colored.
var percent = (interactionTimes) * (lineDivision);
console.log("this is the percent:" + percent);
myProgress.style.strokeDashoffset = 100 - percent;
if (interactionTimes === numberOfInputs) {
myProgress.style.stroke = "#1DEBA1";
} else if (interactionTimes < numberOfInputs) {
myProgress.style.stroke = "purple";
}
return true;
}
checks = document.querySelectorAll("input[type='number']");
checks.forEach(function(paint) {
paint.addEventListener("change", function() {
updateBar()
});
});
#mySvg {
transform: rotate(-180deg);
}
#myRect {
width: 0;
height: 30px;
stroke: #E3E5E7;
stroke-width: 3px;
}
#myProgress {
margin: auto;
width: 50%;
/*stroke: red;*/
stroke-width: 3px;
stroke-dasharray: 100;
stroke-dashoffset: 100;
stroke-linecap: square;
}
<svg id="line-progress" height="4" width="300">
<g >
<line class="myRect" id="myRect" x1="0" y1="50%" x2="100%" y2="50%" stroke-width="4" fill="transparent" />
</g>
<g >
<line class="myProgress" id="myProgress" x1="0" y1="50%" x2="100%" y2="50%" pathLength="100" fill="transparent"/>
</g>
</svg>
<form>
<label> Total inputs to interact with
<input id="totalInput" type="number" min="0" max="100">
</label>
<br>
<label> Number of valid inputs
<input id="validInput" type="number" min="0" max="100">
</label>
</form>
<script src="script.js"></script>
I have a usecase where designers supply us with a SVG, and we use certain elements in that SVG to position our dynamically created elements.
In the snippet below I try to overlap the rect#overlayTarget with the div#overlay using getBoundingClientRect: it doesn't take the scaling of the parent element into account, and the elements don't overlap.
The answers from this question is not applicable here as it uses element.offsetLeft and element.offsetTop, which aren't available for SVG: How to compute getBoundingClientRect() without considering transforms?
How do I make the #overlay and #overlayTarget overlap?
const target = document.querySelector("#overlayTarget");
const position = target.getBoundingClientRect();
const overlay = document.querySelector("#overlay");
overlay.style.top = `${position.y}px`;
overlay.style.left = `${position.x}px`;
overlay.style.width = `${position.width}px`;
overlay.style.height = `${position.height}px`;
#overlay {
position: absolute;
background: hotpink;
opacity: 0.3;
width: 100px;
height: 100px;
}
<div id="app" style="transform: scale(0.875);">
Test
<div id="overlay"></div>
<svg xmlns="http://www.w3.org/2000/svg" width="1809" height="826" viewBox="0 0 809 826">
<g
id="Main_overview"
data-name="Main overview"
transform="translate(-49.5 -155)"
>
<g
id="overlayTarget"
data-name="DC-DC converter"
transform="translate(400 512)"
>
<rect
id="Rectangle_29"
data-name="Rectangle 29"
width="74"
height="74"
fill="none"
stroke="#47516c"
stroke-width="2"
/>
</g>
</g>
</svg>
</div>
If you cannot set your overlay element outside of the transformed element, this answer will work, but only for some simple transformations:
translations and
scales with factors > 0
In these cases, the corners of the bounding box aren't moved out of their top/left and bottom/right orientation. Rotations or skews, and most of the 3D transforms won'T work out.
You can then compute the resulting box values for your overlay by transforming the corners of position with the inverse matrix to that set for the #app element. The DOMPoint and DOMMatrix interfaces help with that.
It is important to remember that transform sets an implicit position: relative, so the top and left values of the overlay are not in relation to the viewport.
const app = document.querySelector('#app');
const relative = app.getBoundingClientRect();
const target = document.querySelector("#overlayTarget");
const position = target.getBoundingClientRect();
const matrix = new DOMMatrix(app.style.transform).inverse();
const topleft = new DOMPoint(
position.x - relative.x,
position.y - relative.y
).matrixTransform(matrix);
const bottomright = new DOMPoint(
position.x - relative.x + position.width,
position.y - relative.y + position.height
).matrixTransform(matrix);
const overlay = document.querySelector("#overlay");
overlay.style.top = `${topleft.y}px`;
overlay.style.left = `${topleft.x}px`;
overlay.style.width = `${bottomright.x - topleft.x}px`;
overlay.style.height = `${bottomright.y - topleft.y}px`;
#overlay {
position: absolute;
background: hotpink;
opacity: 0.3;
width: 100px;
height: 100px;
}
<div id="app" style="transform: scale(0.875);">
Test
<div id="overlay"></div>
<svg xmlns="http://www.w3.org/2000/svg" width="1809" height="826" viewBox="0 0 809 826">
<g
id="Main_overview"
data-name="Main overview"
transform="translate(-49.5 -155)"
>
<g
id="overlayTarget"
data-name="DC-DC converter"
transform="translate(400 512)"
>
<rect
id="Rectangle_29"
data-name="Rectangle 29"
width="74"
height="74"
fill="none"
stroke="#47516c"
stroke-width="2"
/>
</g>
</g>
</svg>
</div>
I have a div containing an SVG image of 300x300 px and a viewbox of 1000x1000.
The image describes a blue rectangle on top of a red one.
When I move the mouse a circle is following the mouse position inside the image:
Everything is perfect except that when I apply a transformation changing perspective and rotation, the mouse pointer and circle center are not anymore matching:
Code is here:
$(function() {
$('#image').mousemove(function(event) {
var svg = document.querySelector('svg');
var pt = svg.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
pt = pt.matrixTransform(svg.getScreenCTM().inverse());
overlay = document.getElementById('overlay');
$('#overlay').html(
"<circle cx='" + pt.x + "' cy='" + pt.y + "' r='50' stroke='#8f00ff' fill='transparent' stroke-width='10' /></svg>"
);
refresh = $("#overlay").html();
$("#overlay").html( refresh )
});
});
function Transform() {
$('#image').css({
transformOrigin: '500px 500px',
transform: 'perspective(100px) rotateX(5deg)'
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='image' tabindex='0' >
<svg id='svgmap' width='300' height='300' xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 1000 1000'>
<rect x='0' y='0' width='1000' height='1000' fill='red' />
<rect x='250' y='250' width='500' height='500' stroke='yellow' fill='blue' stroke-width='10' />
<g id='overlay'></g>
</svg>
</div>
<button onclick='Transform()'>Transform</button>
My goal is to preserver matching between the purple circle center and the mouse pointer, even when a transformation is applied to the object.
Is there a way to do it?
In your code #image is a div. In order to make it work you need to apply the transformation to the svg element (#svgmap) and the transformation must be an svg transformation.
$(function() {
$('#svgmap').mousemove(function(event) {
var svg = document.querySelector('svg');
var pt = svg.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
pt = pt.matrixTransform(svg.getScreenCTM().inverse());
overlay = document.getElementById('overlay');
$('#overlay').html(
"<circle cx='" + pt.x + "' cy='" + pt.y + "' r='50' stroke='#8f00ff' fill='transparent' stroke-width='10' /></svg>"
);
refresh = $("#layer_wafer").html();
$("#layer_wafer").html( refresh )
});
});
function Transform() {
svgmap.setAttributeNS(null,"transform", "skewX(-20) translate(100)");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='image' tabindex='0' >
<svg id='svgmap' width='300' height='300' xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 1000 1000' transform="">
<rect x='0' y='0' width='1000' height='1000' fill='red' />
<rect x='250' y='250' width='500' height='500' stroke='yellow' fill='blue' stroke-width='10' />
<g id='overlay'></g>
</svg>
</div>
<button onclick='Transform()'>Transform</button>
I understand that you are needing a 3D css transformation but this (at least for now) doesn't work.
This is an article where you can read more about 3d transforms in svg: https://oreillymedia.github.io/Using_SVG/extras/ch11-3d.html In the article you can read: All the 3D transformation functions described in this section should be considered “future”
I've solved the issue in the ugliest possible way. Simply hiding the mouse cursor over the div.
Ugly. But effective.
$(function() {
$('#image').mousemove(function(event) {
var svg = document.querySelector('svg');
var pt = svg.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
pt = pt.matrixTransform(svg.getScreenCTM().inverse());
overlay = document.getElementById('overlay');
$('#overlay').html(
"<circle cx='" + pt.x + "' cy='" + pt.y + "' r='50' stroke='#8f00ff' fill='transparent' stroke-width='10' /></svg>"
);
refresh = $("#overlay").html();
$("#overlay").html( refresh )
});
});
function Transform() {
$('#image').css({
transformOrigin: '500px 500px',
transform: 'perspective(100px) rotateX(5deg)'
});
}
div#image {
cursor: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='image' tabindex='0' >
<svg id='svgmap' width='300' height='300' xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 1000 1000'>
<rect x='0' y='0' width='1000' height='1000' fill='red' />
<rect x='250' y='250' width='500' height='500' stroke='yellow' fill='blue' stroke-width='10' />
<g id='overlay'></g>
</svg>
</div>
<button onclick='Transform()'>Transform</button>
There's a little bit of optimization that I have to do to reduce the mouse cursor position gap between the DIV over and any other page element, but it's acceptable to me.
I build this pen , and it is work on my path image .
//Circle
$(document).on('click','#Change_Green_Circle',function(e){
e.preventDefault();
var img = document.getElementById('imgCircle');
img.style.fill = 'Green';
});
$(document).on('click','#Change_Yellow_Circle',function(e){
e.preventDefault();
var img = document.getElementById('imgCircle');
img.style.fill = 'Yellow';
});
// Wiki
$(document).on('click','#Change_Green_wiki',function(e){
e.preventDefault();
var img = document.getElementById('imgWiki');
img.style.fill = 'Green';
});
$(document).on('click','#Change_Yellow_wiki',function(e){
e.preventDefault();
var img = document.getElementById('imgWiki');
img.style.fill = 'Yellow';
});
//Rest
$(document).on('click','#Rest',function(e){
e.preventDefault();
var circle = document.getElementById('imgCircle');
var wiki = document.getElementById('imgWiki');
imgWiki.style.fill = 'Red';
imgCircle.style.fill = 'Black';
});
#import "compass/css3";
.logo {
width: 200px;
height: 164px;
}
body {
padding: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg id="imgCircle" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="612px" height="502.174px" viewBox="0 65.326 612 502.174" enable-background="new 0 65.326 612 502.174"
xml:space="preserve" class="logo">
<ellipse class="ground" cx="283.5" cy="487.5" rx="259" ry="80"/>
<path id="imgWiki" style="fill: red" d="M210.333,65.331C104.367,66.105-12.349,150.637,1.056,276.449c4.303,40.393,18.533,63.704,52.171,79.03
c36.307,16.544,57.022,54.556,50.406,112.954c-9.935,4.88-17.405,11.031-19.132,20.015c7.531-0.17,14.943-0.312,22.59,4.341
c20.333,12.375,31.296,27.363,42.979,51.72c1.714,3.572,8.192,2.849,8.312-3.078c0.17-8.467-1.856-17.454-5.226-26.933
c-2.955-8.313,3.059-7.985,6.917-6.106c6.399,3.115,16.334,9.43,30.39,13.098c5.392,1.407,5.995-3.877,5.224-6.991
c-1.864-7.522-11.009-10.862-24.519-19.229c-4.82-2.984-0.927-9.736,5.168-8.351l20.234,2.415c3.359,0.763,4.555-6.114,0.882-7.875
c-14.198-6.804-28.897-10.098-53.864-7.799c-11.617-29.265-29.811-61.617-15.674-81.681c12.639-17.938,31.216-20.74,39.147,43.489
c-5.002,3.107-11.215,5.031-11.332,13.024c7.201-2.845,11.207-1.399,14.791,0c17.912,6.998,35.462,21.826,52.982,37.309
c3.739,3.303,8.413-1.718,6.991-6.034c-2.138-6.494-8.053-10.659-14.791-20.016c-3.239-4.495,5.03-7.045,10.886-6.876
c13.849,0.396,22.886,8.268,35.177,11.218c4.483,1.076,9.741-1.964,6.917-6.917c-3.472-6.085-13.015-9.124-19.18-13.413
c-4.357-3.029-3.025-7.132,2.697-6.602c3.905,0.361,8.478,2.271,13.908,1.767c9.946-0.925,7.717-7.169-0.883-9.566
c-19.036-5.304-39.891-6.311-61.665-5.225c-43.837-8.358-31.554-84.887,0-90.363c29.571-5.132,62.966-13.339,99.928-32.156
c32.668-5.429,64.835-12.446,92.939-33.85c48.106-14.469,111.903,16.113,204.241,149.695c3.926,5.681,15.819,9.94,9.524-6.351
c-15.893-41.125-68.176-93.328-92.13-132.085c-24.581-39.774-14.34-61.243-39.957-91.247
c-21.326-24.978-47.502-25.803-77.339-17.365c-23.461,6.634-39.234-7.117-52.98-31.273C318.42,87.525,265.838,64.927,210.333,65.331
z M445.731,203.01c6.12,0,11.112,4.919,11.112,11.038c0,6.119-4.994,11.111-11.112,11.111s-11.038-4.994-11.038-11.111
C434.693,207.929,439.613,203.01,445.731,203.01z"/>
<filter id="pictureFilter" >
<feGaussianBlur stdDeviation="15" />
</filter>
</svg>
<button type="button" id="Change_Green_Circle">Change Circle to Green</text>
<button type="button" id="Change_Yellow_Circle">Change Circle to Yellow</text>
<button type="button" id="Change_Green_wiki">Change wiki to Green</text>
<button type="button" id="Change_Yellow_wiki">Change Wiki to Yellow</text>
<button type="button" id="Rest">Rest </text>
BUT
I want to allow the user to upload SVG in my site, and to do the same function i did in my pen above, so I need to take the path of image uploaded by the user , HOW ?
1- which programming language i need to use ?
2- how to take the path
of the uploaded SVg image by user
check out this fiddle: http://jsfiddle.net/recouwnh/19/
What does fiddle do:
01) Retrieve details of dragged SVG image by user.
02) Then convert that image data into SVG tags, with appropriate tags like circle, path etc.
03) On click of "Change Color" button, script finds all path elements from viewing SVG and give them random colors.
HTML Code:
<div class="viewer">
<label class="viewer-switch"> </label>
<div class="alert-error"></div>
<div class="introbox"></div>
<div class="viewer-wrap"></div>
<a style="display: none;"></a>
<p>Drop your content here</p>
</div>
<div>
<button type="button" name="btn_changecolor" id="btn_changecolor">Change Color</button>
</div>
JS Code:
var viewerCtrl = new ViewerController(document.querySelector('.viewer'));
// Cheap way to inform the user about errors.
var alertBoxTimeout, alertBox = document.querySelector('.alert-error');
window.onerror = function (msg) {
alertBox.textContent = msg;
alertBox.classList.add('show');
if (!alertBoxTimeout) {
setTimeout(function () {
alertBox.classList.remove('show');
}, 8000);
}
};
$('#btn_changecolor').on('click', function(){
var min=0, max=255, random1 = 0, random2 = 0, random3 = 0;
$('.viewer-wrap svg path').each(function(){
random1 = Math.random() * (+max - +min) + +min;
random2 = Math.random() * (+max - +min) + +min;
random3 = Math.random() * (+max - +min) + +min;
this.style.fill = 'rgb('+ random1 +', '+ random2 +', '+ random3 +')';
});
});
Taken reference from
01) https://maxwellito.github.io/vivus-instant/ (SVG Animation Playground)
02) Original github link which created above SVG animation:
https://github.com/maxwellito/vivus
03) https://maxwellito.github.io/vivus-instant/scripts/ViewerCtrl.js (Used to create draggable area & prepare SVG from the data which is getting read from user uploading image)
Note: Have checked working of this fiddle with SVG only. For changing the colors of specific path elements, you need to find out your own way.
Hope this will be useful too you.
I need to do my custom triangle progress bar using SVG and Angular JS. But it seems to be hard to control the green color bar. Can anyone help me?
Here my codes. You may adjust the value in the textbox.
var app = angular.module('ProgressBar', []);
app.controller('ProgressBarCtrl', function($scope) {
$scope.A=365;
$scope.B=275;
$scope.C=33;
$scope.D=276;
$scope.E=366;
$scope.F=157;
});
.bar-content{fill:#D1D3D4;}
.bar-frame{fill:#69BD45;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ProgressBar" ng-controller="ProgressBarCtrl">
<input type="number" ng-model="A" />
<input type="number" ng-model="B" />
<input type="number" ng-model="C" />
<input type="number" ng-model="D" />
<input type="number" ng-model="E" />
<input type="number" ng-model="F" />
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.3 841.9" style="enable-background:new 0 0 595.3 841.9;" xml:space="preserve">
<polygon id="XMLID_1_" class="bar-content" points="535,275 36,275 535,97 "/>
<polygon id="XMLID_2_" class="bar-frame" points="{{A}},{{B}} {{C}},{{D}} {{E}},{{F}} "/>
</svg>
</div>
Haven't touched Angular before, so I can't help you there. I can however present an approach suitable for use with either SVG or Canvas. (The canvas implementation is faster, I imagine since it's GPU accelerated)
Since your initial image has an aspect ratio of 2.85 : 1, I chose to use a height of 100px and a width of 285px - I've used the same dimensions for each.
function byId(id){return document.getElementById(id)}
function allByClass(clss){return document.getElementsByClassName(clss)}
function allByTag(tag,parent){return (parent = undefined ? document : parent).getElementsByTagName(tag)}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
byId('slider').style.width = byId('volume').width + 'px';
setVolume(50);
byId('slider').addEventListener('input', onSliderChanged, false);
byId('slider').addEventListener('input', onSlider2Changed, false);
}
function onSliderChanged(evt)
{
var value = this.value;
setVolume(value);
}
function onSlider2Changed(evt)
{
var value = this.value;
setVolumeSVG(value);
}
function setVolumeSVG(percent)
{
var svg = byId('mSvg');
var barWidth = (percent/100) * svg.width.baseVal.value;
var barHeight = (percent/100) * svg.height.baseVal.value;
var msg = "0,"+svg.height.baseVal.value + " "
+ barWidth + "," + (svg.height.baseVal.value-barHeight) + " "
+ barWidth + "," + svg.height.baseVal.value;
allByClass('barSlider')[0].setAttribute('points', msg);
}
//
//
// (2)
//
//
//
//
// (1) (3)
function setVolume(percent)
{
var can = byId('volume');
var ctx = can.getContext('2d');
ctx.fillStyle = "rgba(0,0,0,0)";
ctx.fillRect(0,0,can.width,can.height);
ctx.fillStyle = "#d1d3d4";
ctx.moveTo(0,can.height);
ctx.beginPath();
ctx.lineTo(can.width, 0);
ctx.lineTo(can.width,can.height);
ctx.lineTo(0,can.height);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#69bd45";
ctx.moveTo(0,can.height);
ctx.lineTo( (percent/100)*can.width, can.height - ( (percent/100)*can.height) );
ctx.lineTo( (percent/100)*can.width, can.height );
ctx.lineTo(0,can.height);
ctx.fill()
}
<canvas width=285 height=100 id='volume'></canvas><br>
<input type='range' min='0' max='100' step='1' value=50 id='slider'/>
<hr/>
<svg id='mSvg' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 100" width=285 height=100>
<g>
<polygon class="barFrame" points="0,100 285,100 285,0"></polygon>
<polygon class='barSlider' points="0,100 143,100 143,50"></polygon>
</g>
<style>
.barFrame{ fill: #d1d3d4; }
.barSlider{ fill: #69bd45; }
</style>
</svg>