Reducing area of div to a circle? - javascript

While giving a div equal height and width and setting a border radius of 100% makes a div "look" like a circle, the reality is that it is still a square in disguise. How can I make a div a "true" circle? For my purpose, I want to make items droppable only within the circle I create. By making a div look like a circle, items are still droppable outside of the circle at the edges.
$(".circle").droppable();
$(".drop").draggable({
containment: "parent"
});
.circle
{
width: 200px;
height: 200px;
border: 1px solid black;
border-radius: 100%;
display: inline-block;
}
.drop
{
border: 1px solid black;
display: inline-block;
}
.drop:hover
{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div class="circle">
<div class="drop">
Drop Me<br>
Outside
</div>
</div>

overflow: hidden;
box-sizing: border-box;
padding: 50px;
for container would do the thing :)
or if you can do it with flexbox then
overflow: hidden;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
use svg object like this, as suggested in comment
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle fill="#FFF" cx="50" cy="50" r="50"/></svg>

I can propose to handle dragging manually and prevent unexpected behaviour if an element goes out of a circle. Add some additional javascript.
The following code can deal with just one corner (left-top). You can add three more conditions to handle all corners. It would be better to move a small circle rather than a rectangle - a circle has just one condition, rectangle has four ones.
// radius of a circle
var radius = 100;
$(".circle").droppable();
$(".drop").draggable({
containment: "parent",
drag: function( event, ui ) {
// position of a left top corner of a rectangle with respect
// to a center of a circle
var x = ui.position.left - radius,
y = radius - ui.position.top,
r = Math.sqrt(x*x + y*y);
// if distance from a center of a circle is greater than radius
// move it back into the circle
if (Math.floor(r) > radius) {
ui.position.top = radius - Math.round(radius * y / r);
ui.position.left = Math.round(radius * x / r) + radius;
}
}
});
.circle
{
width: 200px;
height: 200px;
border: 1px solid black;
border-radius: 50%;
display: inline-block;
}
.drop
{
border: 1px solid black;
display: inline-block;
}
.drop:hover
{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div class="circle">
<div class="drop">
Drop Me<br>
Outside
</div>
</div>
UPD. Example of dragging circle was considered in another question.

Related

css triangle based on page height

Currently I have the situation as shown below in the snippet.
But now I want a triangle that is the same on every page. No matter how long the page is. So for example if the page is really long, then the triangle will at one point go out of the screen and there will be no more green background. (as shown here)
But the most important thing is that on every page the triangle/angle will be the same. How to do this?
$(document).ready(function() {
function waitForElement(elementPath, callBack) {
window.setTimeout(function() {
if ($(elementPath).length) {
callBack(elementPath, $(elementPath));
} else {
waitForElement(elementPath, callBack);
}
}, 300)
}
waitForElement("#leftdiv", function() {
// Initial background height set to be equal to leftdiv
$('#rightdiv').height($('#leftdiv').height());
// Initial triangle height set to be equal to leftdiv
$('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
});
// When window resizes
$(window).resize(function() {
// Change height of background
$('#rightdiv').height($('#leftdiv').height());
// Change height of white triangle
$('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
});
});
.container-general {
float: left;
position: relative;
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
float: right;
position: relative;
/*height is set in javascript*/
width: 30%;
background-color: green;
}
#triangle {
position: absolute;
height: 0;
width: 0;
bottom: 0;
left: -1px;
border-left: 10vw solid white;
border-right: 0px solid transparent;
/*border-top is set in javascript*/
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div id="triangle"></div>
</div>
You don't need JavaScript and jQuery at all for this, as long as you are willing to make minor changes to your markup:
Step 1: Update your markup
Wrap both your .container-general and .background-general with a common parent element
Use display: flex; overflow: hidden; on the parent. This has the effect of stretching the shorter background element to full height of .container-general
Step 2: Determine the fixed angle you want and set aspect ratio
Important note: If you want to keep the angle constant, you will need to know what angle you want. That will require one important trick: you want to keep .background-general the same aspect ratio in all cases, so the angle stays constant. Let's say you want it to be 60° (i.e. Math.Pi / 3): with some math, that means that the height of the .background-general should be this ratio relative to the width:
containerHeightRatioToWidth = Math.tan(Math.PI / 3) = 1.732052602783882...
There is a trick to preserve the aspect ratio: you simply set the padding-bottom of the background element. In this case, you want it to be padding-bottom: 173%); (we don't need absolute precision so we can drop the decimal points).
Here's a handy table on the height (in CSS percentages) you can use:
30deg: padding-bottom: 57%:
45deg: padding-bottom: 100%:
60deg: padding-bottom: 173%:
You can also precalculate the percentage in your browser console by pasting this:
var desiredAngleInDegrees = 60;
Math.tan(Math.PI * desiredAngleInDegrees / 180) * 100
The markup is structured as follows:
└─┬.wrapper
├──.container-general
└─┬.background-general
└─┬.background-general__background
├─::before (triangle)
└─::after (remaining fill)
To achieve the triangle effect, you have two approaches:
Step 3A: Use clip-path to trim the background element to look like a triangle
clip-path is very widely supported by modern browsers, with a notable exception for IE11 and Edge :/ This should do the trick: clip-path: polygon(100% 0, 0 0, 100% 100%);
.wrapper {
display: flex;
overflow: hidden;
}
.container-general {
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
position: relative;
width: 30%;
background-color: green;
overflow: hidden;
}
.background-general__background {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/* Triangle */
.background-general__background::before {
flex-grow: 0;
content: '';
display: block;
width: 100%;
padding-bottom: 173%;
background-color: white;
clip-path: polygon(0 100%, 0 0, 100% 100%);
}
/* Extra fill */
.background-general__background::after {
flex-grow: 1;
content: '';
display: block;
background-color: white;
/* Needed to fix subpixel rendering */
margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div class="background-general__background"></div>
</div>
</div>
Step 3B: Use an inline SVG as background image
For the greater browser compatibility, use an inline encoded SVG and stretch it to 100% width and 100% height of the parent.
We can create a simple 10×10px SVG of the following markup:
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10">
<path fill="green" d="M0,0 L10,0 L10,10 z"></path>
</svg>
Note: The preserveAspectRatio="none" is required so that we can freely stretch the SVG beyond its usual aspect ratio. For more information of how the <path>'s d attribute works, see this article: The SVG path Syntax: An Illustrated Guide
Then, all you need is to stuff this short SVG markup as data:image/svg+xml for the background image of the background container, i.e.:
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="green" d="M0,0 L10,0 L10,10 z"></path></svg>');
See example below:
.wrapper {
display: flex;
overflow: hidden;
}
.container-general {
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
position: relative;
width: 30%;
background-color: green;
overflow: hidden;
}
.background-general__background {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/* Triangle */
.background-general__background::before {
content: '';
display: block;
flex-grow: 0;
width: 100%;
padding-bottom: 173%;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="white" d="M0,0 L0,10 L10,10 z"></path></svg>');
background-size: 100% 100%;
}
/* Extra fill */
.background-general__background::after {
flex-grow: 1;
content: '';
display: block;
background-color: white;
/* Needed to fix subpixel rendering */
margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div class="background-general__background"></div>
</div>
</div>
A simple "border triangle" bind to vw units might do:
body {
min-height: 2000px;
}
#triangle {
position: absolute;
top: 0px;
right: 0px;
border-top: 100vw solid #ff0000; /* The height of the triangle */
border-left: 30vw solid transparent; /* The width of the triangle */
}
<div id="triangle"></div>
A fiddle to play with.

How to maintain upper left corner with skew, without using transform? [duplicate]

Possibly more math than CSS, but I'm trying to determine a method for adjusting the positioning of a div after a CSS skewY transform has been applied to it.
In the snippet below, the div with the blue border has a 3.5deg skewY applied, and I'd like to know if there is a mathematical way to know how much top to apply to the blue div so that the top right corner is always perfectly aligned to the top right corner of the div with the red border, regardless of the width of the two divs.
I have played around with numbers using % and vw, but I'm looking for a solid math-based solution.
.parent {
border: 1px solid red;
position: relative;
margin-top: 100px;
height: 200px;
}
.child {
border: 1px solid blue;
position: absolute;
width: 100%;
height: 100%;
transform: skewY(-3.5deg);
}
<div class="parent">
<div class="child">
content
</div>
</div>
No need math, simply adjust transform-origin:
.parent {
border: 1px solid red;
position: relative;
margin-top: 100px;
height: 200px;
}
.child {
border: 1px solid blue;
position: absolute;
width: 100%;
height: 100%;
transform: skewY(-3.5deg);
transform-origin:top right;
}
<div class="parent">
<div class="child">
content
</div>
</div>
But if you want to play with math the exact formula is :
top = tan(Xdeg)*(width/2)
green is the top, purple is half the width and yellow is the angle of skew
In this case we have -3.5deg so the tan(-3.5deg) = -0.061 so top = -0.061 * 50% of width BUT since the reference of the div is top left when applying top property we need to consider a minus sign because we want to adjust the top right corner and not the top left one
.parent {
border: 1px solid red;
position: relative;
display:inline-block;
height: 100px;
width:var(--w); /*Used fixed width to make calculation easy*/
}
.child {
border: 1px solid blue;
position: absolute;
width: 100%;
height: 100%;
transform: skewY(-3.5deg);
top:calc(0.061 * (var(--w) / 2));
}
<div class="parent" style="--w:200px;">
<div class="child">
content
</div>
</div>
<div class="parent" style="--w:100px;">
<div class="child">
content
</div>
</div>

How to create a square shaped div inside an arbitrary circle?

I am using Javascript to create an SVG element that contains a circle with a radius and a stroke thickness. The size and thickness may vary. I'm trying to create a square shaped div that would fit inside this SVG circle, so that I may add content inside the circle.
You can imagine the content to be anything from a text containing information about the circle, an anchor, or a button.
The rectangle must fit in the circle in so that all content is wrapped, and if there is no space, the content will be removed.
Here is the raw Sketch
<!-- A minified example of what the Javascript outputs -->
<svg viewBox="0 0 80 80" width="80" height="80">
<circle cx="40" cy="40" r="35"></circle>
</svg>
My main question is if it's possible to add this solely to the SVG element, and using something like the styling: left: 10%; top: 10%; width:50%; height: 50%, or if this would require more advanced CSS or Javascript trickery.
It's important to also mention that my circle has a radius of (svgWidth / 2) * 0.875 that is set from within the Javascript code.
Okay, thanks to #Sergiu I found the right mathematical equation to solve it, this was the primary issue. The code below is taken out of my Javascript code and shows how I create a rect that fits exactly like the square my image.
var squareSize = Math.sqrt(2) * radius - circleStrokeThickness;
var squareCenter = (svgWidth - squareSize) / 2;
this.rectangleContent = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
this.rectangleContent.setAttribute('x', squareCenter);
this.rectangleContent.setAttribute('y', squareCenter);
this.rectangleContent.setAttribute('width', squareSize);
this.rectangleContent.setAttribute('height', squareSize);
this.rectangleContent = $(this.rectangleContent).appendTo(this.svg);
This is not a div but it already answers all of the questions I had about the placement of the div.
I believe this is what you are looking for. You can resize the SVG and see everything resizes accordingly.
.container {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.container svg {
fill: #dedede;
stroke: #000;
width: 200px;
height: 200px;
overflow: visible;
background-color: lightblue;
border: 1px solid blue;
}
.container svg g > .text-holder {
background-color: lightcoral;
}
.container svg g > .text-holder > p {
font-size: 12px;
}
.container svg g > circle {
cx: 50%;
cy: 50%;
r: 50%;
}
.container svg g > rect {
stroke: #f00;
x: 15%;
y: 15%;
width: 70%;
height: 70%;
}
<div class="container">
<svg viewBox="0 0 80 80">
<g>
<circle></circle>
<rect></rect>
<foreignObject class="text-holder" x="15%" y="15%" width="70%" height="70%">
<p xmlns="http://www.w3.org/1999/xhtml" style="font-size: 12px;">Text goes here</p>
</foreignObject>
</g>
</svg>
</div>

jQuery draggable moves when dropped

I have set up a simple drag and drop interface which implements the clone functionality. The issue I have is that when the element is dragged to the main canvas and then dropped, it suddenly shifts to the right, rather than dropping exactly where I drag it.
Here is the javascript:
$(function() {
var x = null;
$("#draggable").draggable({
helper: 'clone',
cursor: 'move',
snap: '.snap-target'
});
$("#canvas").droppable({
drop: function(e, ui) {
if ($(ui.draggable)[0].id != "") {
x = ui.helper.clone();
ui.helper.remove();
x.draggable({
helper: 'original',
containment: '#canvas',
snap: '.snap-target'
});
x.appendTo('#canvas');
}
}
});
});
I have created a jsfiddle:
https://jsfiddle.net/kuyn6gmc/
If you try and drag the blue box into the main canvas then release the mouse you will see how the box "pops" to the right slightly. When you move the box within the canvas, it works fine. On the fiddle it's not as bad as when it's full width on a browser, I think it's relative to the total width of the viewport.
If anyone knows why this may be happening I would appreciate it :)
Thank you
Michael
The reason this is occuring is because the draggable element is absolutely positioned relative to the viewport while it is being dragged. Once the element is appended, the positioning is relative to the #canvas element (because of position: relative), which is why the element moves when you drop it.
As the other answer suggests, you could remove position: relative from the element, however, that probably won't work in all cases. I'd suggest taking the positioning of the element into account before appending the element.
Updated Example
For instance, you could subtract the offset top/left positioning, as well as the width of the border. In doing so, the #canvas element can still be relatively positioned.
var canvasOffset = {
'top': parseInt($(this).offset().top, 10) + parseInt($(this).css('border-top-width'), 10),
'left': parseInt($(this).offset().left, 10) + parseInt($(this).css('border-left-width'), 10)
}
$draggble.css({
"top": parseInt($draggble.css('top'), 10) - canvasOffset.top + 'px',
"left": parseInt($draggble.css('left'), 10) - canvasOffset.left + 'px'
}).appendTo('#canvas');
You have to put position: relative to the body instead of the canvas.
.draggable { padding: 5px; float: left; margin: 0 10px 10px 0; font-size: .9em; position: relative; }
.small { border: 1px solid slateblue; width: 115px; height: 115px; background: #c5cae9 }
.ui-widget-header p, .ui-widget-content p { margin: 0; }
.text-box { text-align: center; vertical-align: middle; }
body { font-family: 'Helvetica Neue', Helvetica, Arial, serif; font-size: 13px }
#canvas { width: 600px; height: 300px; border: 1px solid #cccccc; margin: auto;
clear: both; }
.draggable-selectors { margin: 10px auto; width: 1000px; }
body { position: relative; margin: 0; }
<body class='snap-target'>

calculate radius of a circle using javascript

I am trying to calculate radius of a circle using javascript. I have following section with css
.circle {
position: absolute;
width: 100px;
height: 100px;
border-radius: 70px;
background: red;
}
<section class="circle"></section>
As the width and height of this circle is 100x100. How can I calculate its radius?
Since the radius is just half of the diameter, this is easy. The diameter is 100px, per width and height. Hence, radius is 100px / 2 = 50px.
While you could set the radius relatively by border-radius: 50%, you could simply divide the width/height of the box by 2 to get the radius.
For instance:
var circle = document.querySelector('.circle'),
radius = circle.offsetWidth / 2;
circle.innerHTML = "Radius: " + radius + "px";
.circle {
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%; /* I don't know if you really need to get the value of this */
background: red;
line-height: 100px;
text-align: center;
}
<section class="circle"></section>
If you just need to set a radius to make a perfect circle, use 50% radius. This way it doesn't depend on width/height and you don't need javascript:
.circle {
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
background: red;
}
<section class="circle"></section>

Categories

Resources