You may have seen JavaScript sliders before:
http://dev.jquery.com/view/tags/ui/1.5b2/demos/ui.slider.html
What I'm envisioning is a circular slider. It would consist of a draggable button at one point on the circle -- and that button can be dragged anywhere along the ring. The value depends on what position the button is at (think of a clock).
define a center point c
current mouse point at m
in your mouse drag event handler, you'd have
var dx = m.x-c.x;
var dy = m.y-c.y;
var scale = radius/Math.sqrt(dx*dx+dy*dy);
slider.x = dx*scale + c.x;
slider.y = dy*scale + c.y;
radius would be some preset value of the slider,
How about this: http://www.thewebmasterservice.com/dev-blog/curved-rounded-or-circular-sliders-javascript-jquery-ui
Some explanations and a demo.
I have written a jQuery plugin which supports full/half circle sliders.
Demo Page & Documentation
Check the jQuery roundSlider plugin from here http://roundsliderui.com/. This is what you want exactly.
This roundslider having the similar options like the jQuery ui slider. It support default, min-range and range slider type. Not only the round slider it also supports various circle shapes such as quarter, half and pie circle shapes.
For more details check the demos and documentation page.
Please check the demo from jsFiddle.
Related answers: https://stackoverflow.com/a/31367164/1845801 and https://stackoverflow.com/a/31369265/1845801
Screenshots:
Related
I have been struggling for a while with a Angular application that has one component with a map. To be able to rule out any other problems I have done a rewrite in pure javascript to test the functionality but I can't figure out how to solve this.
What I want to do is the same kind of functionality as in for example Google Maps, If you have the cursor over a specific city and zoom in with the mouse scroll then the map zooms in but keeps the city at the same place under the cursor.
I have this code and it's the function "zoomImage" that give me problems..
https://codepen.io/m-rten-sw-rd/pen/BMZvZX
Anyone that could guide me right on this?
function zoomImage(scale, mousePosInCointainer, mousePosInImage) {
var imgElement = document.getElementById("img");
imgElement.width = imgElement.width * scale;
imgElement.height = imgElement.height * scale;
/* TODO: Determine how I'm going to center the image over cursor */
}
I believe the following is what you need to be doing.
Determine transform origin (should be center) of the image.
Determine difference between mouse cursor and that point.
Scale that difference by your scale factor.
Translate the image by that difference.
My code of svg.js use rotate action and move action, but two results dont have the same center coordinate
Here is my code
<body>
<div id="drawing"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.js"></script>
<script type="text/javascript">
var draw = SVG('drawing')
var group_1 = draw.group()
var group_2 = draw.group()
var rect_1 = group_1.rect(50, 20).fill('#f06').center(50, 50)
var rect_2 = group_2.rect(50, 20).fill('#f09').center(50, 50)
rect_1.animate(1000).rotate(45).after(function(){
group_1.animate(1000).center(100, 100)})
group_2.animate(1000).center(100, 100)
</script>
</body>
In SVG groups needs to be understood as a grouping of elements. Basically the same as in photohop. You can select multiple elements and rotate or scale them at once.
That in turn means that groups do not have geometry on its own. It fully depends on the content where the group is visible on the screen (x, y) or how bif it is (with/height).
Thats the reason, why you cannot move a group. You only can transform it. To keep the api simple, svg.js gives you the handy move and center method which - under the hood - translate the group.
In your example, you move the group after you rotated it. But you do it with an absolute transformation. That means svg.js tries to incooperate the movement into the already present rotation. This maths does not goes off well sometimes.
To fix your problem you have to use relative transformations instead.
Which means the movement is ON TOP of the rotation. That also means that you have to figure out by how much you wanna move the group.
We are currently working on version 3 of svg.js which simplifies this transformation business alot. So I hope the final solution will be there soon
I have two canvases. I have made them circular using border-radius. The 2nd is positioned inside the first one (using absolute position).
I have click events on both circles. If you click on inside canvas, the color at the point of the click is loaded in the outside canvas with opacity varying from white to the picked color and finally to black. If you click on outer canvas the exact color value at that point is loaded in the text-box at the bottom
I am unable to click in red zones (as shown in figure below) of the outer canvas when using chrome. I tried z-idex, arcs but nothing is helping me. But In Firefox everything is working fine.
Note: You can drag the picker object in the outer circle. But if you leave it in red zones, you would not be able to click it again in Chrome. Clicking in green zone will get you its control again
Code in this JSFiddle
Edit
I excluded all irrelevant code to make it easy. Now there is only a container having two canvas.
Filled simply with two distinct colors. Open following fiddle link in both chrome and firefox. Click on both cirles in different zones and see difference in chrome and firefox. I want them to behave in chrome as they do in firefox
Note I will ultimately draw an image in inner canvas.
Updated Fiddle Link
-
Your problem is because canvases currently are always rectangular, even if they don't look rectangular. Border radius makes the edges except the circle transparent, but it still doesn't stop events in Chrome on the corner areas. This is why you cannot click the bottom circle in those areas
I even tried putting it inside of a container that had a border-radius instead but the click event still goes through
With that being said, you have two options. You could either change your code to only use one canvas with the same type of layout, just drawing the background circle before the other each time. Essentially you'd draw a circle, draw your black to color to white gradient, use the xor operation to combine the two into one circle, then do the same with the rainbox gradient. You must draw the background circle first because canvas paints over the old layers every time
or
You could use javascript to only detect clicks in the circular area which takes just a little bit of math (: This solution is featured in edit below
In the future, CSS Shapes may allow canvases to be non-rectangular elements to be used, I'm actually not sure, but we don't have that capability yet at least
Edit
Alright, so after going through your code a bit it seems there are some things I should cover before I offer a solution
Setup all your finite variables outside of the functions that run every time. This means you don't put them (like radiuses, offsets, etc.) in the click function or something that runs often since they don't change
Your "radius"es are actually "diameter"s. The format of .rect goes .rect(x, y, width (diameter of circle), height (diameter of circle))
Almost always when overlaying canvases like you are you want to make them equal dimensions and starting position to prevent calculation error. In the end it makes it easier, doing all relative positioning with javascript instead of mixing it with CSS. In this case, however, since you're using border-radius instead of arc to make a circle, keep it like it is but position it using javascript ....
jQuery isn't needed for something this simple. If you're worried about any load speed I'd recommend doing it in vanilla javascript, essentially just changing the .click() functions into .onclick functions, but I left jQuery for now
You can declare multiple variables in a row without declaring var each time by using the following format:
var name1 = value1,
name2 = value2;
Variables with the same value you can declare like so:
var name1 = name2 = sameValue;
When children have position:absolute and you want it to be positioned relative to the parent, the parent can have position:relative, position:fixed, or position:absolute. I would think you'd want position:relative in this case
When you don't declare var for a variable it becomes global (unlessed chained with a comma like above). For more on that read this question
Now, onto the solution.
After talking with a friend I realized I could sort do the math calculation a lot easier than I originally thought. We can just calculate the center of the circles and use their radiuses and some if statements to make sure the clicks are in the bounds.
Here's the demo
After everything is set up correctly, you can use the following to detect whether or not it's in the bounds of each
function clickHandler(e, r) {
var ex = e.pageX,
ey = e.pageY,
// Distance from click to center
l = Math.sqrt(Math.pow(cx - ex, 2) + Math.pow(cy - ey, 2));
if(l > r) { // If the distance is greater than the radius
if(r === LARGE_RADIUS) { // Outside of the large
// Do nothing
} else { // The corner area you were having a problem with
clickHandler(e, LARGE_RADIUS);
}
} else {
if(r === LARGE_RADIUS) { // Inside the large cirle
alert('Outer canvas clicked x:' + ex + ',y:' + ey);
} else { // Inside the small circle
alert('Inner canvas clicked x:' + ex + ',y:' + ey);
}
}
}
// Just call the function with the appropriate radius on click
$(img_canvas).click(function(e) { clickHandler(e, SMALL_RADIUS); });
$(wheel_canvas).click(function(e) { clickHandler(e, LARGE_RADIUS); });
Hopefully the comments above and code make enough sense, I tried to clean it up as best as I could. If you have any questions don't hesitate to ask!
I have a working Google Combochart. Now i want to remove the highlight on specific moments when i hover over an item in the legend. I used the option "enableInteractivity", but this also removes things like the tooltips.
I want to keep the tooltips of course. I can't seem to find anything on the internet about how this is possible. I know i can disable the tooltips only, but not a method to disable this highlighting (what i want)..
Anybody has an idea how i can achieve this?
Thanks in advance!
Following this approach for styling on hover, you can get rid of the grey outline highlighting on hover by applying this css style.
#mychart svg g g g g rect {
stroke-width:0px;
}
There is no built-in method to do this. The highlights are drawn in to the SVG and the tooltips are also drawn by the google internal charts API code. So either you'd have to find a way to stop the highlights from being drawn in to the SVG (while still allowing the tooltips), or to disable interactivity and implement your own tooltips like this answer does. Answer quoted below (by jeffery_the_wind):
I ended up making a custom tool-tip pop-up that is working pretty
well.
Assuming your div for the bubble chart is defined like this:
<div id="bubble_chart_div"></div>
Then I used the JavaScript below. Please note that I left out that
stuff about how to set up your google chart data and loading the
google charts package. This code just shows how to get your custom
toolip popup.
var mouseX;
var mouseY;
$(document).mousemove( function(e) {
mouseX = e.pageX;
mouseY = e.pageY;
});
/*
*
*instantiate and render the chart to the screen
*
*/
var bubble_chart = new google.visualization.BubbleChart(document.getElementById('bubble_chart_div'));
bubble_chart.draw(customer_product_grid_data_table, options);
/*
* These functions handle the custom tooltip display
*/
function handler1(e){
var x = mouseX;
var y = mouseY - 130;
var a = 1;
var b = 2;
$('#custom_tooltip').html('<div>Value of A is'+a+' and value of B is'+b+'</div>').css({'top':y,'left':x}).fadeIn('slow');
}
function handler2(e){
$('#custom_tooltip').fadeOut('fast');
}
/*
* Attach the functions that handle the tool-tip pop-up
* handler1 is called when the mouse moves into the bubble, and handler 2 is called when mouse moves out of bubble
*/
google.visualization.events.addListener(bubble_chart, 'onmouseover', handler1);
google.visualization.events.addListener(bubble_chart, 'onmouseout', handler2);
I want to add zoom feature my app . I use Kinetic js and somewhere I found solutions for this feature but I can't apply these solution for some reason . I tried to adapt the solutions but unsuccesful . I have many Kinetic.Layer , some of them will scale when zooming apply. my challenge is that : zoom will happen on mouse position . solution that I found gives me : layer.setPosition() after scaling . As I mentioned before , I must not use "layer.setPosition" I will do this as using stage.setPosition() but I couldn't calculate new x and y of position 100% accurately. Could anyone suggest me any solution way ?
What you really want to do when zooming is to set the scale.
You can set the scale for any layer, node, or the entire stage. Just do:
layer1.setScale(2,2); // this doubles the layer size from the original
This doesn't affect any other layer, so your overlay will stay in place.
In addition, you should also do:
layer1.setPosition(x,y); // this will move the layer to the fixed point you want.
All together you could do:
function zoom(){
var position = stage.getUserPosition();
layer1.setScale(2,2);
layer1.setPosition(position.x - layer2.getX(), position.y - layer2.getY()); //move the layer by an offset based on the second layer. This isn't exactly correct so it's something you have to experiment with.
}
Check out this: http://jsfiddle.net/TFU7Z/1/ Maybe is what you are looking for, I did not quite understand the question.
var zoom = function(e) {
var zoomAmount = 1;
layer.setScale(layer.getScale().x+zoomAmount)
layer.draw();
}
document.addEventListener("click", zoom, false)
Just click anywhere to zoom. You can attach the "click" event listener to whatever part of the stage / document you want.
These answers seems not to work awith the KineticJS 5.1.0. These do not work mainly for the signature change of the scale function:
stage.setScale(newscale); --> stage.setScale({x:newscale,y:newscale});
However, the following solution seems to work with the KineticJS 5.1.0:
JSFiddle: http://jsfiddle.net/rpaul/ckwu7u86/3/