HTML 5 Using canvas in flexbox causes weird scaling - html

When using canvas in a flexbox and drawing rects of the same size it appears to be glitching.
Take a look at the following example:
It draws a one by one pixel rect to your cursor location but the drawn rectangles are of different size relative to the distance of the top left corner.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.addEventListener("click", (event) => {
let x = event.offsetX;
let y = event.offsetY;
ctx.fillRect(x, y, x + 1, y + 1);
});
#container {
display: flex;
}
#first {
flex: 1 1 auto;
background-color: green;
width: 60px;
}
#second {
flex: 2 1 auto;
background-color: red;
}
canvas {
object-fit:none;
object-position:top left;
}
#third {
flex: 1 1 auto;
background-color: green;
width: 60px;
}
<div id="container">
<div id="first"></div>
<div id="second">
<canvas id="canvas" height="160px"></canvas>
</div>
<div id="third"></div>
</div>
How do I prevent this from happening?

Check out https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillRect. The third and fourth arguments for fillRect are the height and width, not positions. So if I understand correctly, you would want these both to just be 1 if you are looking for a 1x1 square.

Related

Div height based on rendered character size

Essentially I'm wondering whether there's any mechanism to map your font's characters to a div's dimensions, taller letters creating taller divs, etc
There's units like em, ex, and ch that are supposed to represent font size but these are the font as a whole, not relative to the present characters
This may just not be possible within css/html
I've created this code example of what I mean, red being the div bounds, blue being my desired output
body {
margin : 5px 0 0 5px;
padding : 0;
display : inline-flex;
flex-flow : column;
}
.set {
display : inline-flex;
flex-flow : row;
padding : 0;
}
.overlay {
position : absolute;
margin : 5px 0px 5px 5px;
width : 60px;
box-shadow : 0 0 0 2px blue;
}
.text {
margin : 5px 5px 5px 5px;
font-family : sans-serif;
font-size : 32px;
line-height : 32px;
box-shadow : 0 0 0 2px red;
width : 60px;
text-align : center;
}
.overlay.overlay-1 {
margin-top : -26px;
height : 13px;
}
.overlay.overlay-2 {
margin-top : -34px;
height : 22px;
}
.overlay.overlay-3 {
margin-top : -28px;
height : 23px;
}
<html>
<body>
<div class="set">
<div>
<div class="text">xxx</div>
</div>
<div>
<div class="text">xxx</div>
<div class="overlay overlay-1"></div>
</div>
</div>
<div class="set">
<div>
<div class="text">Xxx</div>
</div>
<div>
<div class="text">Xxx</div>
<div class="overlay overlay-2"></div>
</div>
</div>
<div class="set">
<div>
<div class="text">xxy</div>
</div>
<div>
<div class="text">xxy</div>
<div class="overlay overlay-3"></div>
</div>
</div>
</body>
</html>
It seems like there is no programatic way of achieving this with CSS, as the relative heights in CSS refer to the font dimensions, not individual characters.
Edit: found a Javascript code that converts a line of text into an image, then measures the image size in pixels. Maybe you can use that.
Check this post
function measureTextHeight(fontSizeFace) {
// create a temp canvas
var width = 1000;
var height = 60;
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
// Draw the entire a-z/A-Z alphabet in the canvas
var text = "o";
ctx.save();
ctx.font = fontSizeFace;
ctx.clearRect(0, 0, width, height);
ctx.fillText(text, 0, 40);
ctx.restore();
// Get the pixel data from the canvas
var data = ctx.getImageData(0, 0, width, height).data,
first = false,
last = false,
r = height,
c = 0;
// Find the last line with a non-transparent pixel
while (!last && r) {
r--;
for (c = 0; c < width; c++) {
if (data[r * width * 4 + c * 4 + 3]) {
last = r;
break;
}
}
}
// Find the first line with a non-transparent pixel
while (r) {
r--;
for (c = 0; c < width; c++) {
if (data[r * width * 4 + c * 4 + 3]) {
first = r;
break;
}
}
// If we've got it then return the height
if (first != r) return last - first;
}
// error condition if we get here
return 0;
}

Can animation only be applied to 'absolute' positioned elements?

I am trying to animate an object using DOM and struggling to animate the element when its CSS property position is not set to "absolute". Here is my code below:
I create a circle HTML element and try to move it in 45 degrees. Is there any way to animate an HTML element object that is not positioned absolute?
x = 10;
function on_click() {
var myCurvyMovement = document.getElementById("circle");
myCurvyMovement.style.left = 0.5 * x;
myCurvyMovement.style.top = 1 + x
x += 10;
}
#circle {
position: absolute;
width: 100px;
height: 100px;
background: red;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
}
/* Cleaner, but slightly less support: use "50%" as value */
#divBox {
position: static
}
<body>
<button style="display:block" onclick="on_click()">Move the box</button>
<div id="circle">
</div>
</body>
I wouldn't consider left/right in order to do animation. As you have noticed, it won't work in all the cases as it need positionned elements. Even when using positionned element you won't have the same behavior between relative, absolute and fixed because each one will have its own reference for top/left.
For such case better consider transform that you can apply to any element (shouldn't be an inline element) and the reference of the movement will be the same for all. You will also have better performance.
x = 10;
function on_click() {
var myCurvyMovement = document.getElementById("circle");
myCurvyMovement.style.transform = "translate(" + (0.5 * x)+"px,"+(1 + x)+"px)";
x += 10;
}
#circle {
width: 100px;
height: 100px;
background: red;
border-radius: 50px;
transition:0.5s all; /*to have a smooth movement*/
}
<body>
<button style="display:block" onclick="on_click()">Move the box</button>
<div id="circle">
</div>
</body>
You forgot to concatenate the "px" to set the x and y positions
x = 10;
function on_click() {
var myCurvyMovement = document.getElementById("circle");
myCurvyMovement.style.left = 0.5 * x + 'px';
myCurvyMovement.style.top = 1 + x + 'px';
x += 10;
}
#circle {
position: absolute;
width: 100px;
height: 100px;
background: red;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
}
/* Cleaner, but slightly less support: use "50%" as value */
#divBox {
position: static
}
<body>
<button style="display:block" onclick="on_click()">Move the box</button>
<div id="circle">
</div>
</body>
when not's absolute you need change the margin-left and margin-top property, in javascript is like this
myCurvyMovement.style.marginLeft = 1 + x + 'px'
myCurvyMovement.style.marginTop = 1 + x + 'px'
(top/bottom and left/rigth)

CSS position elements on the outside of a circle [duplicate]

How can I position several <img> elements into a circle around another and have those elements all be clickable links as well? I want it to look like the picture below, but I have no idea how to achieve that effect.
Is this even possible?
2020 solution
Here's a more modern solution I use these days.
I start off by generating the HTML starting from an array of images. Whether the HTML is generated using PHP, JS, some HTML preprocessor, whatever... this matters less as the basic idea behind is the same.
Here's the Pug code that would do this:
//- start with an array of images, described by url and alt text
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- } /* and so on, add more images here */
- ];
- let n_imgs = imgs.length;
- let has_mid = 1; /* 0 if there's no item in the middle, 1 otherwise */
- let m = n_imgs - has_mid; /* how many are ON the circle */
- let tan = Math.tan(Math.PI/m); /* tangent of half the base angle */
.container(style=`--m: ${m}; --tan: ${+tan.toFixed(2)}`)
- for(let i = 0; i < n_imgs; i++)
a(href='#' style=i - has_mid >= 0 ? `--i: ${i}` : null)
img(src=imgs[i].src alt=imgs[i].alt)
The generated HTML looks as follows (and yes, you can write the HTML manually too, but it's going to be a pain to make changes afterwards):
<div class="container" style="--m: 8; --tan: 0.41">
<a href='#'>
<img src="image_mid.jpg" alt="alt text"/>
</a>
<a style="--i: 1">
<img src="first_img_on_circle.jpg" alt="alt text"/>
</a>
<!-- the rest of those placed on the circle -->
</div>
In the CSS, we decide on a size for the images, let's say 8em. The --m items are positioned on a circle and it's if they're in the middle of the edges of a polygon of --m edges, all of which are tangent to the circle.
If you have a hard time picturing that, you can play with this interactive demo which constructs the incircle and circumcircle for various polygons whose number of edges you pick by dragging the slider.
This tells us that the size of the container must be twice the radius of the circle plus twice half the size of the images.
We don't yet know the radius, but we can compute it if we know the number of edges (and therefore the tangent of half the base angle, precomputed and set as a custom property --tan) and the polygon edge. We probably want the polygon edge to be a least the size of the images, but how much we leave on the sides is arbitrary. Let's say we have half the image size on each side, so the polygon edge is twice the image size. This gives us the following CSS:
.container {
--d: 6.5em; /* image size */
--rel: 1; /* how much extra space we want between images, 1 = one image size */
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
--s: calc(2*var(--r) + var(--d)); /* container size */
position: relative;
width: var(--s); height: var(--s);
background: silver /* to show images perfectly fit in container */
}
.container a {
position: absolute;
top: 50%; left: 50%;
margin: calc(-.5*var(--d));
width: var(--d); height: var(--d);
--az: calc(var(--i)*1turn/var(--m));
transform:
rotate(var(--az))
translate(var(--r))
rotate(calc(-1*var(--az)))
}
img { max-width: 100% }
See the old solution for an explanation of how the transform chain works.
This way, adding or removing an image from the array of images automatically arranges the new number of images on a circle such that they're equally spaced out and also adjusts the size of the container. You can test this in this demo.
OLD solution (preserved for historical reasons)
Yes, it is very much possible and very simple using just CSS. You just need to have clear in mind the angles at which you want the links with the images (I've added a piece of code at the end just for showing the angles whenever you hover one of them).
You first need a wrapper. I set its diameter to be 24em (width: 24em; height: 24em; does that), you can set it to whatever you want. You give it position: relative;.
You then position your links with the images in the center of that wrapper, both horizontally and vertically. You do that by setting position: absolute; and then top: 50%; left: 50%; and margin: -2em; (where 2em is half the width of the link with the image, which I've set to be 4em - again, you can change it to whatever you wish, but don't forget to change the margin in that case).
You then decide on the angles at which you want to have your links with the images and you add a class deg{desired_angle} (for example deg0 or deg45 or whatever). Then for each such class you apply chained CSS transforms, like this:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
where you replace {desired_angle} with 0, 45, and so on...
The first rotate transform rotates the object and its axes, the translate transform translates the object along the rotated X axis and the second rotate transform brings back the object into position.
The advantage of this method is that it is flexible. You can add new images at different angles without altering the current structure.
CODE SNIPPET
.circle-container {
position: relative;
width: 24em;
height: 24em;
padding: 2.8em;
/*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
border: dashed 1px;
border-radius: 50%;
margin: 1.75em auto 0;
}
.circle-container a {
display: block;
position: absolute;
top: 50%; left: 50%;
width: 4em; height: 4em;
margin: -2em;
}
.circle-container img { display: block; width: 100%; }
.deg0 { transform: translate(12em); } /* 12em = half the width of the wrapper */
.deg45 { transform: rotate(45deg) translate(12em) rotate(-45deg); }
.deg135 { transform: rotate(135deg) translate(12em) rotate(-135deg); }
.deg180 { transform: translate(-12em); }
.deg225 { transform: rotate(225deg) translate(12em) rotate(-225deg); }
.deg315 { transform: rotate(315deg) translate(12em) rotate(-315deg); }
<div class='circle-container'>
<a href='#' class='center'><img src='image.jpg'></a>
<a href='#' class='deg0'><img src='image.jpg'></a>
<a href='#' class='deg45'><img src='image.jpg'></a>
<a href='#' class='deg135'><img src='image.jpg'></a>
<a href='#' class='deg180'><img src='image.jpg'></a>
<a href='#' class='deg225'><img src='image.jpg'></a>
<a href='#' class='deg315'><img src='image.jpg'></a>
</div>
Also, you could further simplify the HTML by using background images for the links instead of using img tags.
EDIT: example with fallback for IE8 and older (tested in IE8 and IE7)
Here is the easy solution without absolute positioning:
.container .row {
margin: 20px;
text-align: center;
}
.container .row img {
margin: 0 20px;
}
<div class="container">
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
</div>
http://jsfiddle.net/mD6H6/
Using the solution proposed by #Ana:
transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)
I created the following jsFiddle that places circles dynamically using plain JavaScript (jQuery version also available).
The way it works is rather simple:
document.querySelectorAll( '.ciclegraph' ).forEach( ( ciclegraph )=>{
let circles = ciclegraph.querySelectorAll( '.circle' )
let angle = 360-90, dangle = 360 / circles.length
for( let i = 0; i < circles.length; ++i ){
let circle = circles[i]
angle += dangle
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth / 2}px) rotate(-${angle}deg)`
}
})
.ciclegraph {
position: relative;
width: 500px;
height: 500px;
margin: calc(100px / 2 + 0px);
}
.ciclegraph:before {
content: "";
position: absolute;
top: 0; left: 0;
border: 2px solid teal;
width: calc( 100% - 2px * 2);
height: calc( 100% - 2px * 2 );
border-radius: 50%;
}
.ciclegraph .circle {
position: absolute;
top: 50%; left: 50%;
width: 100px;
height: 100px;
margin: calc( -100px / 2 );
background: teal;
border-radius: 50%;
}
<div class="ciclegraph">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
Building off #Ana's excellent answer, I created this dynamic version that allows you to add and remove elements from the DOM and maintain proportionate spacing between the elements - check out my fiddle: https://jsfiddle.net/skwidbreth/q59s90oy/
var list = $("#list");
var updateLayout = function(listItems) {
for (var i = 0; i < listItems.length; i++) {
var offsetAngle = 360 / listItems.length;
var rotateAngle = offsetAngle * i;
$(listItems[i]).css("transform", "rotate(" + rotateAngle + "deg) translate(0, -200px) rotate(-" + rotateAngle + "deg)")
};
};
$(document).on("click", "#add-item", function() {
var listItem = $("<li class='list-item'>Things go here<button class='remove-item'>Remove</button></li>");
list.append(listItem);
var listItems = $(".list-item");
updateLayout(listItems);
});
$(document).on("click", ".remove-item", function() {
$(this).parent().remove();
var listItems = $(".list-item");
updateLayout(listItems);
});
#list {
background-color: blue;
height: 400px;
width: 400px;
border-radius: 50%;
position: relative;
}
.list-item {
list-style: none;
background-color: red;
height: 50px;
width: 50px;
position: absolute;
top: 50%;
left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<ul id="list"></ul>
<button id="add-item">Add item</button>
Here is a version I made in React from the examples here.
CodeSandbox Example
import React, { useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const graph = useRef(null);
useEffect(() => {
const ciclegraph = graph.current;
const circleElements = ciclegraph.childNodes;
let angle = 360 - 90;
let dangle = 360 / circleElements.length;
for (let i = 0; i < circleElements.length; i++) {
let circle = circleElements[i];
angle += dangle;
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth /
2}px) rotate(-${angle}deg)`;
}
}, []);
return (
<div className="App">
<div className="ciclegraph" ref={graph}>
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
</div>
</div>
);
}
You can certainly do it with pure css or use JavaScript. My suggestion:
If you already know that the images number will never change just calculate your styles and go with plain css (pros: better performances, very reliable)
If the number can vary either dynamically in your app or just may vary in the future go with a Js solution (pros: more future-proof)
I had a similar job to do, so I created a script and open sourced it here on Github for anyone who might need it. It just accepts some configuration values and simply outputs the CSS code you need.
If you want to go for the Js solution here's a simple pointer that can be useful to you. Using this html as a starting point being #box the container and .dot the image/div in the middle you want all your other images around:
Starting html:
<div id="box">
<div class="dot"></div>
<img src="my-img.jpg">
<!-- all the other images you need-->
</div>
Starting Css:
#box{
width: 400px;
height: 400px;
position: relative;
border-radius: 100%;
border: 1px solid teal;
}
.dot{
position: absolute;
border-radius: 100%;
width: 40px;
height: 40px;
left: 50%;
top: 50%;
margin-left: -20px;
margin-top: -20px;
background: rebeccapurple;
}
img{
width: 40px;
height: 40px;
position: absolute;
}
You can create a quick function along these lines:
var circle = document.getElementById('box'),
imgs = document.getElementsByTagName('img'),
total = imgs.length,
coords = {},
diam, radius1, radius2, imgW;
// get circle diameter
// getBoundingClientRect outputs the actual px AFTER transform
// using getComputedStyle does the job as we want
diam = parseInt( window.getComputedStyle(circle).getPropertyValue('width') ),
radius = diam/2,
imgW = imgs[0].getBoundingClientRect().width,
// get the dimensions of the inner circle we want the images to align to
radius2 = radius - imgW
var i,
alpha = Math.PI / 2,
len = imgs.length,
corner = 2 * Math.PI / total;
// loop over the images and assign the correct css props
for ( i = 0 ; i < total; i++ ){
imgs[i].style.left = parseInt( ( radius - imgW / 2 ) + ( radius2 * Math.cos( alpha ) ) ) + 'px'
imgs[i].style.top = parseInt( ( radius - imgW / 2 ) - ( radius2 * Math.sin( alpha ) ) ) + 'px'
alpha = alpha - corner;
}
You can see a live example here
There is no way to magically place clickable items in a circle around another element with CSS.
The way how I would do this is by using a container with position:relative;. And then place all the elements with position:absolute; and using top and left to target it's place.
Even though you haven't placed jquery in your tags it might be best to use jQuery / javascript for this.
First step is placing your center image perfectly in the center of the container using position:relative;.
#centerImage {
position:absolute;
top:50%;
left:50%;
width:200px;
height:200px;
margin: -100px 0 0 -100px;
}
After that you can place the other elements around it by using an offset() of the centerImage minus the offset() of the container. Giving you the exact top and left of the image.
var left = $('#centerImage').offset().left - $('#centerImage').parent().offset().left;
var top = $('#centerImage').offset().top - $('#centerImage').parent().offset().top;
$('#surroundingElement1').css({
'left': left - 50,
'top': top - 50
});
$('#surroundingElement2').css({
'left': left - 50,
'top': top
});
$('#surroundingElement3').css({
'left': left - 50,
'top': top + 50
});
What I've done here is placing the elements relative to the centerImage. Hope this helps.
You could do it like this: fiddle
Don't mind the positioning, its a quick example
The first step is to have 6 long columnar boxes:
The second step is to use position: absolute and move them all into the middle of your container:
And now rotate them around the pivot point located at the bottom center. Use :nth-child to vary rotation angles:
div {
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
rotate: (360deg / 6) * $n;
}
}
Now all you have to do is to locate your images at the far end of every column, and compensate the rotation with an anti-rotation :)
Full source:
<div class="flower">
<div class="petal">1</div>
<div class="petal">2</div>
<div class="petal">3</div>
<div class="petal">4</div>
<div class="petal">5</div>
<div class="petal">6</div>
</div>
.flower {
width: 300px;
height: 300px;
// We need a relative position
// so that children can have "position:abolute"
position: relative;
.petal {
// Make sure petals are visible
border: 1px solid #999;
// Position them all in one point
position: absolute; top: 0; left: 50%;
display: inline-block;
width: 30px; height: 150px;
// Rotation
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
// Petal rotation
$angle: (360deg / 6) * $n;
rotate: $angle;
// Icon anti-rotation
.icon { rotate: -$angle; }
}
}
}
}
See CodePen

Dynamically Scaling SVG

I'm working on a site where users can manipulate an SVG image through a couple of textboxes.
I would like to have the SVG scale to fit the container div.
For example, if the SVG was exactly the container's height and 10 pixels wide, then doubling the height would cause the apparent width to be 5 pixels.
My page is split roughly in half, with the numbers on the left and the image on the right. Resizing the browser thus causes the SVG's container element to change shape, meaning that I can't hardcode the container's dimensions in the SVG.
Every solution I've found online uses the viewBox attribute; however, I can't find a way to apply that without having a hard-coded container size.
Here is a fiddle with my editor setup:
https://jsfiddle.net/xyjs5b63/
Adjusting viewBox sounds like what you want. I'm not sure what you were doing that made it not work.
var svg = document.querySelector('svg');
var inputs = document.querySelectorAll('input');
var height_elem = inputs[0];
var width_elem = inputs[1];
height_elem.value = '100';
width_elem.value = '100';
height_elem.addEventListener("change", valueChange);
width_elem.addEventListener("change", valueChange);
function valueChange() {
svg.setAttribute('viewBox', "0 0 "+width_elem.value+" "+height_elem.value);
}
valueChange();
#out {
width: 100px;
height: 100px;
background-color: honeydew;
}
svg {
width: 100%;
height: 100%;
}
<div id="main">
<div id="in">
<input type="number"><br>
<input type="number">
</div>
<div id="out">
<svg>
<rect width="100%" height="100%"></rect>
</svg>
</div>
</div>
var rect = document.querySelector('rect');
var svg = document.querySelector('svg');
var inputs = document.querySelectorAll('input');
var height_elem = inputs[0];
var width_elem = inputs[1];
height_elem.value = '100';
width_elem.value = '100';
height_elem.addEventListener("change", valueChange);
width_elem.addEventListener("change", valueChange);
function valueChange() {
max = parseInt(height_elem.value) >= parseInt(width_elem.value) ? 'h' : 'w';
if (max == 'h') {
rect.setAttribute('height', "100%");
rect.setAttribute('width', (width_elem.value * 100 / height_elem.value)+"%");
}
else {
rect.setAttribute('width', "100%");
rect.setAttribute('height', (height_elem.value * 100 / width_elem.value)+"%");
}
}
valueChange();
#main {
width: 100%;
padding: 0;
}
#in {
float: left;
width: 40%;
height: 100%
}
#out {
margin: 10%;
width: 20vw;
height: 20vw;
}
svg {
width: 100%;
height: 100%;
}
<div id="main">
<div id="in">
Height: <input type="number"><br>
Width: <input type="number">
</div>
<br>
<div id="out">
<svg height="auto">
<rect></rect></svg>
</div>
</div>
Does this solve your problem?

Setting limit for mootools makeResizable dynamically?

I'm trying to set the upper limit values of a resizable image to keep it within the containing div. I'm using mootools to make the image both moveable and resizable (implementing Drag.Move and makeResizable to do so.)
My temporary solution is to use overflow:hidden; so the resized image does not overtake the rest of the page when it is sized beyond the container, but I'd like to be able to have a way so the image can not be resized outside of its container.
I know that since limit is set on 'domready', if I try to set it to a variable that changes value as the image is resized (ie: onDrag), the limit parameter won't be updated on the fly. I'm wondering if anyone has any insight into how I can achieve a similar effect to the Drag.Move container parameter, as makeResizable doesn't seem to have the same parameter.
HTML:
<div id="pImageArea">
<div id="pLogo" class="displayNone">
<div id="moveHandleName">
<img src="uploadedlogo.jpg" id="imgName" />
</div>
<div id="resizeHandleName"></div>
</div>
</div>
CSS:
#imageArea {
float: left;
width: 630px;
height: 400px;
border: 1px solid #333;
position: relative;
overflow: hidden;
}
#imgContainer {
width: 50px;
height: 50px;
border: 1px dashed #333;
position: absolute;
}
#imgName {
width: 100%;
}
#moveHandleName {
width: 100%;
height: 100%;
}
#resizeHandleName {
width: 8px;
height: 8px;
border: 1px solid #000;
position: absolute;
left: 100%;
top: 100%;
margin: -5px 0 0 -5px;
background-color: #fff;
z-index: 100;
}
JS:
window.addEvent('domready', function(){
var xLim = 50;
var yLim = 50;
// Make image moveable
new Drag.Move($('imgContainer'), {
container: $('imageArea'),
handle: $('imgHandleName')
});
// Make image resizable
$('imgContainer').makeResizable({
handle:$('handleName'),
limit: {x: [50, xLim], y: [50, yLim]},
onDrag: function(el) {
// Set imgContainer height
el.setStyle('height', $('imgName').getSize().y + 'px');
// Set upper limits
xLim = $('imageArea').getSize().x - el.getSize().x;
yLim = $('imageArea').getSize().y - el.getSize().y;
},
});
});
Thanks in advance,
Matt
I 'solved' it like this in my own code (modified to use your element and values):
$('imgContainer').retrieve('resizer').setOptions({
limit: {
x: [50, xLim],
y: [50, yLim]
}
});
The move limit should be properly enforced once you specify the 'container' option and your container has a set width and height. When you resize, my draggable does not need to have limits (re)set. (Using Moo 1.4 by the way.)
However, resizing does cause problems in conjunction with move and limits. The key is that the 'limit' option is only set when initializing makeResizable(). The only way to update it is by setting it with the code shown above. But you have to update it right after you dropped the draggable, because that's the only event that affects the limit. So:
// Make image moveable
new Drag.Move($('imgContainer'), {
container: $('imageArea'),
handle: $('imgHandleName'),
onDrop: function() {
$('imgContainer').retrieve('resizer').setOptions({
limit: {
x: [50, $('imageArea').getSize().x - parseInt($('imageArea]).getStyle('left'))],
y: [50, $('imageArea').getSize().y - parseInt($('imageArea]).getStyle('top'))]
}
});
});
As you can see, I've also used getStyle() instead of getPosition() because getPosition() returns a value relative to the window, while Moo sets the draggable top and left relative to the droparea.