Creating dynamic 3d css box based on height, width and depth - javascript

I have created the 3D Box with Fixed height and width, Now i have to make
it dynamic based height, width and depth given by user so that he can get
idea of how the box will look like. Height and width is working fine but when
i try to change the depth the box design breaks. also i want it to rotate
the box from the center position which is not done if i change the width.
jQuery('._3dface--top').css("width", (jQuery('#boxWidth').val() * 10));
jQuery('._3dface--top').css("height", jQuery('#boxzPosition').val());
jQuery('._3dface--bottom').css("width", (jQuery('#boxWidth').val() * 10));
jQuery('._3dface--bottom').css("height", jQuery('#boxzPosition').val());
jQuery('._3dface--bottom').css("top", parseInt((jQuery('#boxHeight').val() * 10) - 250));
jQuery('._3dface--left').css("width", jQuery('#boxzPosition').val());
jQuery('._3dface--left').css("height", (jQuery('#boxHeight').val() * 10));
jQuery('._3dface--right').css("width", jQuery('#boxzPosition').val());
jQuery('._3dface--right').css("height", (jQuery('#boxHeight').val() * 10));
jQuery('._3dface--right').css("left", parseInt((jQuery('#boxWidth').val() * 10) - 130));
jQuery('._3dface--back').css("width", (jQuery('#boxWidth').val() * 10));
jQuery('._3dface--back').css("height", (jQuery('#boxHeight').val() * 10));
JSfiddle

I have recreated your idea starting from 0.
I have set a demo cube where all the dimensions are set with css properties, and inheritance where posible.
there are 2 auxiliary elements that have borders to make them visible.
The six faces are background colored with distinctive colors to make them distinguishable.
And the center of rotation will be always where it should, too.
You can adapt it to run in older browsers using jQuery to change the variables, instead of using CSS (supported in all modern browsers. the only issue would be with IE)
const inputs = document.querySelectorAll('input');
// listen for changes
inputs.forEach(input => input.addEventListener('change', update));
function update(e) {
document.documentElement.style.setProperty(`--${this.id}`, this.value + 'px');
}
:root {
--height: 200px;
--width: 300px;
--depth: 120px;
}
.base,
.base * {
transform-style: preserve-3d;
}
.base {
height: var(--height);
width: var(--width);
margin: 100px;
position: relative;
border: solid 1px blue;
transform: rotate3d(1, 1, 1, 45deg);
}
.top {
height: var(--depth);
width: 100%;
bottom: 100%;
position: absolute;
background-color: rgba(255, 0, 0, 0.4);
transform: translateY(50%) rotateX(90deg);
}
.down {
height: var(--depth);
width: 100%;
top: 100%;
position: absolute;
background-color: rgba(0, 255, 0, 0.4);
transform: translateY(-50%) rotateX(90deg);
}
.right {
width: var(--depth);
height: 100%;
left: 100%;
position: absolute;
background-color: rgba(128, 128, 0, 0.4);
transform: translateX(-50%) rotateY(90deg);
}
.left {
width: var(--depth);
height: 100%;
right: 100%;
position: absolute;
background-color: rgba(128, 0, 128, 0.4);
transform: translateX(50%) rotateY(90deg);
}
.aux {
width: 100%;
height: var(--depth);
border: solid 2px red;
position: absolute;
transform: translateY(-50%) rotateX(-90deg);
}
.front {
height: var(--height);
width: 100%;
top: 100%;
position: absolute;
background-color: rgba(0, 0, 255, 0.4);
transform: rotateX(90deg);
transform-origin: center top;
}
.back {
height: var(--height);
width: 100%;
bottom: 100%;
position: absolute;
background-color: rgba(0, 128, 128, 0.4);
transform: rotateX(-90deg);
transform-origin: center bottom;
}
input {
width: 50px;
}
<div class="base">
<div class="top"></div>
<div class="down"></div>
<div class="right"></div>
<div class="left"></div>
<div class="aux">
<div class="front"></div>
<div class="back"></div>
</div>
</div>
<label for="height">height</label>
<input type="number" id="height" value="200" />
<label for="width">width</label>
<input type="number" id="width" value="300" />
<label for="depth">depth</label>
<input type="number" id="depth" value="120" />

Related

Progress bar different colors

how would you make progress bar in CSS that would have colours based on values etc. from 0% to 20% red colour, 20% to 40% blue... Also, I would want to show the colours all the time, not only when it hits the value(so that part of a progress bar would be red, part blue and the other colours from the beggining and that the colours would disappear as the value would go down).
If you are trying to achieve a gradient progress bar as per the current progress, then try linear-gradient() property in CSS.
Here is a working model:
#prog-bar-cont {
width: 75vw;
height: 2.5em;
}
#prog-bar-cont #prog-bar {
background: #ffff;
width: 100%;
height: 100%;
border-color: #000;
border-style: solid;
border-radius: 50px;
overflow: hidden;
position: relative;
}
#prog-bar-cont #prog-bar #background {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
/*Actual Stuff*/
background: linear-gradient(-90deg, violet, #30b3fc, #70dc23, yellow, orange, #ff1076);
-webkit-clip-path: inset(0 100% 0 0);
clip-path: inset(0 100% 0 0);
transition: all 3s;
-webkit-transition: all 3s;
}
#prog-bar-cont:hover #prog-bar #background {
-webkit-clip-path: inset(0 0 0 0);
clip-path: inset(0 0 0 0);
}
<h1>Rainbow Progress Bar</h1>
<p>Try hovering over the bar</p>
<div id='prog-bar-cont'>
<div id="prog-bar">
<div id="background"></div>
</div>
</div>
You can accomplish that by nesting the progress bar in a parent element and applying the css property overflow: hidden.
You can change the width of the class bar-clipper to the desired percentage. i.e. calc(300px * 0.6) will show 60% of the bar.
.bar-clipper {
width: calc(300px * 0.8);
height: 20px;
overflow: hidden;
position: absolute;
}
.bar-wrapper {
width: 300px;
height: 20px;
display: flex;
position: absolute;
}
.bar-wrapper span {
width: 100%;
height: 100%;
}
.bar-wrapper .bar1 {
background-color: #163f5f;
}
.bar-wrapper .bar2 {
background-color: #21639b;
}
.bar-wrapper .bar3 {
background-color: #3caea3;
}
.bar-wrapper .bar4 {
background-color: #f6d65b;
}
.bar-wrapper .bar5 {
background-color: #ed543b;
}
<body>
<div class="bar-clipper">
<div class="bar-wrapper">
<span class="bar1"></span>
<span class="bar2"></span>
<span class="bar3"></span>
<span class="bar4"></span>
<span class="bar5"></span>
</div>
</div>
</body>
Link to fiddle: https://jsfiddle.net/L13yrgbm/

Create Circle with 3 parts and perform action from each part in HTML/css/script

I am new to design UI in html. I have a requirement to design as per the referenced image. I required source code for the same design. Please do the needful.
There are 3 part in a circle. There will be an event while clicking each part.
https://drive.google.com/file/d/0B8QaA3VryqygYU9valJoYm9WSEU/view?usp=sharing
It is possible to create such a segmented circle in CSS. First, you create the circle with border-radius: 50%; on the container element. Than you create your segments with transform:
transform: rotate(-60deg) skewY(30deg) scale(1.2);
Explanation: With rotate you can place each segment on its proper place, with skew you create the needed angle for the circle center and with scale you make sure that the segments fill the circle up to the border. At last you just create an element for the inner circle and you are done.
To make the segments clickable, you can use the onclick event handler or jQuerys click() function.
Also see this question.
.pie {
position: relative;
margin: 1em auto;
border: 4px solid black;
padding: 0;
width: 15em;
height: 15em;
border-radius: 50%;
list-style: none;
overflow: hidden;
}
.slice {
overflow: hidden;
position: absolute;
top: 0;
right: 0;
width: 50%;
height: 50%;
transform-origin: 0% 100%;
border: 2px solid black;
box-sizing: border-box;
}
.slice-contents {
position: absolute;
left: -100%;
width: 200%;
height: 200%;
border-radius: 50%;
}
.slice:nth-child(1) {
transform: rotate(-60deg) skewY(30deg) scale(1.2);
}
.slice:nth-child(1) .slice-contents {
transform: skewY(-30deg); /* unskew slice contents */
background: lightblue;
}
.slice:nth-child(2) {
transform: rotate(60deg) skewY(30deg) scale(1.2);
}
.slice:nth-child(2) .slice-contents {
transform: skewY(-30deg); /* unskew slice contents */
background: lightgreen;
}
.slice:nth-child(3) {
transform: rotate(180deg) skewY(30deg) scale(1.2);
}
.slice:nth-child(3) .slice-contents {
transform: skewY(-30deg); /* unskew slice contents */
background: orange;
}
.inner-pie {
position: absolute;
width: 3em;
height: 3em;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 4px solid black;
background: white;
}
<ul class='pie'>
<li class='slice'>
<div class='slice-contents'>click 1</div>
</li>
<li class='slice'>
<div class='slice-contents'>click 2</div>
</li>
<li class='slice'>
<div class='slice-contents'>click 3</div>
</li>
<li class='inner-pie'>
</li>
<ul>

Css rating circle embedded in PHP code

I'm trying to use a jsfiddle to my page, but I can't figure out how embed it with my existing code.
JSFiddle
HTML
<div class="radial-progress" data-progress="0">
<div class="circle">
<div class="mask full">
<div class="fill"></div>
</div>
<div class="mask half">
<div class="fill"></div>
<div class="fill fix"></div>
</div>
<div class="shadow"></div>
</div>
<div class="inset">
<div class="percentage">
<div class="numbers"><span>-</span><span>0</span><span>0,1</span><span>0,2</span><span>0,3</span><span>0,4</span><span>0,5</span><span>0,6</span><span>0,7</span><span>0,8</span><span>0,9</span><span>1</span><span>1,1</span><span>1,2</span><span>1,3</span><span>1,4</span><span>1,5</span><span>1,6</span><span>1,7</span><span>1,8</span><span>1,9</span><span>2</span><span>2,1</span><span>2,2</span><span>2,3</span><span>2,4</span><span>2,5</span><span>2,6</span><span>2,7</span><span>2,8</span><span>2,9</span><span>3</span><span>3,1</span><span>3,2</span><span>3,3</span><span>3,4</span><span>3,5</span><span>3,6</span><span>3,7</span><span>3,8</span><span>3,9</span><span>4</span><span>4,1</span><span>4,2</span><span>4,3</span><span>4,4</span><span>4,5</span><span>4,6</span><span>4,7</span><span>4,8</span><span>4,9</span><span>5</span><span>5,1</span><span>5,2</span><span>5,3</span><span>5,4</span><span>5,5</span><span>5,6</span><span>5,7</span><span>5,8</span><span>5,9</span><span>6</span><span>6,1</span><span>6,2</span><span>6,3</span><span>6,4</span><span>6,5</span><span>6,6</span><span>6,7</span><span>6,8</span><span>6,9</span><span>7</span><span>7,1</span><span>7,2</span><span>7,3</span><span>7,4</span><span>7,5</span><span>7,6</span><span>7,7</span><span>7,8</span><span>7,9</span><span>8</span><span>8,1</span><span>8,2</span><span>8,3</span><span>8,4</span><span>8,5</span><span>8,6</span><span>8,7</span><span>8,8</span><span>8,9</span><span>9</span><span>9,1</span><span>9,2</span><span>9,3</span><span>9,4</span><span>9,5</span><span>9,6</span><span>9,7</span><span>9,8</span><span>9,9</span><span>10</span></div>
</div>
</div>
JS
$evaluation = 8,5;
$('head style[type="text/css"]').attr('type', 'text/less');
less.refreshStyles();
window.randomize = function() {
$('.radial-progress').attr('data-progress', $evaluation*10);
}
setTimeout(window.randomize, 200);
$('.radial-progress').click(window.randomize);
CSS
.radial-progress {
#circle-size: 120px;
#circle-background: #d6dadc;
#circle-color: #97a71d;
#inset-size: 90px;
#inset-color: #fbfbfb;
#transition-length: 1s;
#shadow: 6px 6px 10px rgba(0,0,0,0.2);
#percentage-color: #97a71d;
#percentage-font-size: 22px;
#percentage-text-width: 57px;
margin: 50px;
width: #circle-size;
height: #circle-size;
background-color: #circle-background;
border-radius: 50%;
.circle {
.mask, .fill, .shadow {
width: #circle-size;
height: #circle-size;
position: absolute;
border-radius: 50%;
}
.shadow {
box-shadow: #shadow inset;
}
.mask, .fill {
-webkit-backface-visibility: hidden;
transition: -webkit-transform #transition-length;
transition: -ms-transform #transition-length;
transition: transform #transition-length;
border-radius: 50%;
}
.mask {
clip: rect(0px, #circle-size, #circle-size, #circle-size/2);
.fill {
clip: rect(0px, #circle-size/2, #circle-size, 0px);
background-color: #circle-color;
}
}
}
.inset {
width: #inset-size;
height: #inset-size;
position: absolute;
margin-left: (#circle-size - #inset-size)/2;
margin-top: (#circle-size - #inset-size)/2;
background-color: #inset-color;
border-radius: 50%;
box-shadow: #shadow;
.percentage {
height: #percentage-font-size;
width: #percentage-text-width;
overflow: hidden;
position: absolute;
top: (#inset-size - #percentage-font-size) / 2;
left: (#inset-size - #percentage-text-width) / 2;
line-height: 1;
.numbers {
margin-top: -#percentage-font-size;
transition: width #transition-length;
span {
width: #percentage-text-width;
display: inline-block;
vertical-align: top;
text-align: center;
font-weight: 800;
font-size: #percentage-font-size;
color: #percentage-color;
}
}
}
}
#i: 0;
#increment: 180deg / 100;
.loop (#i) when (#i <= 100) {
&[data-progress="#{i}"] {
.circle {
.mask.full, .fill {
-webkit-transform: rotate(#increment * #i);
-ms-transform: rotate(#increment * #i);
transform: rotate(#increment * #i);
}
.fill.fix {
-webkit-transform: rotate(#increment * #i * 2);
-ms-transform: rotate(#increment * #i * 2);
transform: rotate(#increment * #i * 2);
}
}
.inset .percentage .numbers {
width: #i * #percentage-text-width + #percentage-text-width;
}
}
.loop(#i + 1);
}
.loop(#i);
}
The problem is that I'm trying to embed this code in a php function that returns a content string.
So, every HTML in the function is a concatenation in the content string, like this:
$content .= ' <div class="etc....." ';
The page is correctly showed (no problem with script location and concatenation) but I receive the echo of the span content in numbers class.
The output is this:
-00,10,20,30,40,50,60,70,80,911,11,21,31,41,51,61,71,81,922,12,22,32,42,52,62,72,82,933,13,23,33,43,53,63,73,83,944,14,24,34,44,54,64,74,84,955,15,25,35,45,55,65,75,85,966,16,26,36,46,56,66,76,86,977,17,27,37,47,57,67,77,87,988,18,28,38,48,58,68,78,88,999,19,29,39,49,59,69,79,89,910
I think that the javascript function is incompatible with the HTML contained in a string.

CSS Circle animation to show percentage

i have a circle and i am showing some text in the middle as demonstrated in the fiddle(JSFIDDLE http://jsfiddle.net/874jgh4v/2/) My requirement is this
I need to animate the outer white border for percentage for example if the percentage is 50% then i need to show that border only around half the circle
I need to show that percentage value on hower for example the text 50% should be shown only on hower preferably with some animation.
.wrapper{padding:30px;}
.circle{
border-radius: 50%;
background:#32a500;
box-shadow: 0px 0px 0px 16px #f1f1f1;
border: 16px solid #f9f9f9;
width:220px;
height:220px;
box-sizing:border-box;
}
.circle:hover {
background:red;
}
<div class="wrapper">
<div class="circle">
<p>Total ROE's</p>
<p>300</p>
<p>70%</p>
</div>
</div>
Any help would be appreciated! Also i would prefer to do this without external libraries , the percentages should support decimal points upto two points.
Try this:
Html
<span class='Progress'>
<div class="Bar">
<div class="Outer">
<div class="Fill"></div>
</div>
<div class="Draw"></div>
<div class="Status"><span></span></div>
</div>
</span>
CSS
.Progress {
position: absolute;
left: 25%;
bottom: 30%;
}
.Progress .Bar {
width: 70px;
height: 70px;
border-radius: 50%;
background-color: #E5E5E5;
position: relative;
}
.Progress .Bar .Outer {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - 35px);
top: calc(50% - 35px);
width: 70px;
height: 70px;
clip: rect(0, 70px, 70px, 35px);
}
.Bar .Outer .Fill {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - 35px);
top: calc(50% - 35px);
width: 70px;
height: 70px;
clip: rect(0, 35px, 70px, 0);
background: #00A0E3;
transform: rotate(60deg);
}
.Progress .Bar .Draw {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - 53.84615px/2);
top: calc(50% - 53.84615px/2);
width: 53.84615px;
height: 53.84615px;
background: #fff;
text-align: center;
display: table;
}
.Progress .Bar .Status {
display: table-cell;
vertical-align: middle;
position: absolute;
margin-left: -100px;
margin-top: -10px;
width: 200px;
height: 200px;
left: 50%;
top: 50%;
text-align: center;
}
.Progress .Bar .Status > span {
font-size: 14px;
font-weight: bold;
color: #00A0E3;
}
.Progress .Bar.halfway {
background-color: #00A0E3;
}
.Progress .Bar.halfway .Outer {
clip: rect(0, 35px, 70px, 0);
}
.Progress .Bar.halfway .Outer .Fill {
clip: rect(0, 70px, 70px, 35px);
background: #E5E5E5;
}
.Progress .Bar.complete.halfway,
.Progress .Bar.complete .Fill
{
background-color: #8cd64c !important;
}
Javascript/JQuery:
$('document').ready(function() {
var progress = function(perc) {
perc = Math.round(perc * 100) / 100; // 2 decimal places
var $bar = $('.Progress .Bar'),
$fill = $('.Progress .Bar .Outer .Fill'),
$status = $('.Progress .Bar .Status span');
$bar.removeClass("halfway").removeClass("complete");
// outer bar
if (perc >= 50) $bar.addClass("halfway");
if (perc >= 100) $bar.addClass("complete");
// progress bar
var degrees = 360 * perc / 100;
$fill.css({
"WebkitTransform": 'rotate(' + degrees + 'deg)',
"-moz-transform": 'rotate(' + degrees + 'deg)'
});
// status
$status.html(perc);
}
// Test it!
progress(10);
setTimeout(function() {
progress(50);
setTimeout(function() {
progress(100);
}, 2000);
}, 2000);
});
Show me the CodePen

scaling a centered div in a scrollable container

I'm trying to do a scale transformation on a div that is centered on a scrollable container div.
The trick i'm using to reflect the new div size after transformation, is using a wrapper and setting the new width/height to it so the parent can show the scrollbars correctly.
.container {
position: relative;
border: 3px solid red;
width: 600px; height: 400px;
background-color: blue;
overflow-x: scroll; overflow-y: scroll;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.wrapper {
order: 1;
background-color: yellow;
}
.content-outer {
width: 300px;
height: 200px;
transform-origin: 50% 50%;
/*transform-origin: 0 0;*/
}
.content-outer.animatted {
animation: scaleAnimation 1s ease-in forwards;
}
.content-outer.animatted2 {
animation: scaleAnimation2 1s ease-in forwards;
}
.content-inner {
width: 300px;
height: 200px;
background: linear-gradient(to right, red, white);
}
if the transformation origin was 0,0 the div is centered without animation jumps but the scrollbars are not correct. if the origin was in the middle both the div location and scrollbars are missed up
I have tried two ways to do the centering, using flexbox (http://jsfiddle.net/r3jqyjLz/1/) and using negative margins
(http://jsfiddle.net/roLf5tph/1/).
Is there a better way to do this ?
Is this what you are after?
I used CSS transitions for the scaling animation.
#centered {
background-image: linear-gradient(to bottom,yellow,red);
height: 200px;
transform: scale(1);
transition: transform 1s ease-out;
width: 200px;
}
#scrollable {
align-items: center;
border: 1px solid red;
display: flex;
height: 300px;
justify-content: center;
overflow: auto;
width: 300px;
}
Update #1
How about this one?
Using old school absolute centering (position absolute)
I see your point about flexbox. It seems that flexbox centering has some limitations when the centered element is larger than its container.
I am assuming that parts of the problem you are having can be summarized as:
You have a complex structure in your inner div which you want to scale as a group. (If it were a single element, this would've been easy with a matrix).
When the inner div is scaled beyond the bounds of the container, you don't get scrollbars without controlling the width/height.
You want the inner div to remain centered and at the same time, scrollbars should reflect the correct position.
With this, there are two (three actually) easy options.
Option 1:
Using the same markup in the question, you could keep the div centered when the scale factor is below 1. And for scale factors above 1, you change it to top-left. the overflow: auto on the container will take care of the scroll on its own because the div being scaled (via transform) is wrapped inside of another div. You don't really need Javascript for this.
This solves your problems 1 and 2.
Fiddle 1: http://jsfiddle.net/abhitalks/c0okhznc/
Snippet 1:
* { box-sizing: border-box; }
#container {
position: relative;
border: 1px solid red; background-color: blue;
width: 400px; height: 300px;
overflow: auto;
}
.wrapper {
position: absolute;
background-color: transparent; border: 2px solid black;
width: 300px; height: 200px;
top: 50%; left: 50%;
transform-origin: top left;
transform: translate(-50%, -50%);
transition: all 1s;
}
.box {
width: 300px; height: 200px;
background: linear-gradient(to right, red, white);
border: 2px solid yellow;
}
.inner { width:50px; height: 50px; background-color: green; }
#s0:checked ~ div#container .wrapper { transform: scale(0.5) translate(-50%, -50%); }
#s1:checked ~ div#container .wrapper { transform: scale(0.75) translate(-50%, -50%); }
#s2:checked ~ div#container .wrapper { transform: scale(1) translate(-50%, -50%); }
#s3:checked ~ div#container .wrapper { top: 0%; left: 0%; transform-origin: top left; transform: scale(1.5) translate(0%, 0%); }
#s4:checked ~ div#container .wrapper { top: 0%; left: 0%; transform-origin: top left; transform: scale(2) ; }
#s5:checked ~ div#container .wrapper { top: 0%; left: 0%; transform-origin: top left; transform: scale(3) ; }
<input id="s0" name="scale" data-scale="0.5" type="radio" />Scale 0.5
<input id="s1" name="scale" data-scale="0.75" type="radio" />Scale 0.75
<input id="s2" name="scale" data-scale="1" type="radio" checked />Scale 1
<input id="s3" name="scale" data-scale="1.5" type="radio" />Scale 1.5
<input id="s4" name="scale" data-scale="2" type="radio" />Scale 2
<input id="s5" name="scale" data-scale="3" type="radio" />Scale 3
<hr>
<div id="container">
<div id="wrapper" class="wrapper">
<div id="box" class="box">
<div class="inner"></div>
</div>
</div>
</div>
But this creates another problem. The overflow:auto will cause a jump/flicker when the div is scaled beyond the container. This can be easily solved by making it overflow:scroll to show the scrollbars at all times (the way you are doing it already). Although, it works like a charm in Firefox, Chrome falters here and doesn't update the scrollbar position. The trick here is to use Javascript to force a reflow by changing the overflow to auto once your scaling completes. So you need to delay it a bit.
Fiddle 2: http://jsfiddle.net/abhitalks/u7sfef0b/
Snippet 2:
$("input").on("click", function() {
var scroll = 'scroll',
scale = +($(this).data("scale"));
if (scale > 1) { scroll = 'auto'; }
setTimeout(fixScroll, 300, scroll);
});
function fixScroll(scroll) { $("#container").css({ overflow: scroll }); }
* { box-sizing: border-box; }
#container {
position: relative;
border: 1px solid red; background-color: blue;
width: 400px; height: 300px;
overflow: scroll;
}
.wrapper {
position: absolute;
background-color: transparent; border: 2px solid black;
width: 300px; height: 200px;
top: 50%; left: 50%;
transform-origin: top left;
transform: translate(-50%, -50%);
transition: all 1s;
}
.box {
width: 300px; height: 200px;
background: linear-gradient(to right, red, white);
border: 2px solid yellow;
}
.inner { width:50px; height: 50px; background-color: green; }
#s0:checked ~ div#container .wrapper { transform: scale(0.5) translate(-50%, -50%); }
#s1:checked ~ div#container .wrapper { transform: scale(0.75) translate(-50%, -50%); }
#s2:checked ~ div#container .wrapper { transform: scale(1) translate(-50%, -50%); }
#s3:checked ~ div#container .wrapper { top: 0%; left: 0%;transform-origin: top left; transform: scale(1.5) translate(0%, 0%); }
#s4:checked ~ div#container .wrapper { top: 0%; left: 0%;transform-origin: top left; transform: scale(2) ; }
#s5:checked ~ div#container .wrapper { top: 0%; left: 0%;transform-origin: top left; transform: scale(3) ; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="s0" name="scale" data-scale="0.5" type="radio" />Scale 0.5
<input id="s1" name="scale" data-scale="0.75" type="radio" />Scale 0.75
<input id="s2" name="scale" data-scale="1" type="radio" checked />Scale 1
<input id="s3" name="scale" data-scale="1.5" type="radio" />Scale 1.5
<input id="s4" name="scale" data-scale="2" type="radio" />Scale 2
<input id="s5" name="scale" data-scale="3" type="radio" />Scale 3
<hr>
<div id="container">
<div id="wrapper" class="wrapper">
<div id="box" class="box">
<div class="inner"></div>
</div>
</div>
</div>
Option 2:
In order to solve problem 3 of keeping the div centered and at the same time maintaining the correct scrollbar position, you have to fallback on Javascript.
The principle remains the same that for scale factors above 1 you need to reset the top-left and translate positions. You would also need to recalc the scaled width/height and then re-assign that to your wrapper div. Then setting the scrollTop and scrollLeft on the container will be as easy as just getting the difference of the wrapper div and the container div.
This solves your problems 1, 2, and 3.
Fiddle 3: http://jsfiddle.net/abhitalks/rheo6o7p/1/
Snippet 3:
var $container = $("#container"),
$wrap = $("#wrapper"),
$elem = $("#box"),
originalWidth = 300, originalHeight = 200;
$("input").on("click", function() {
var factor = +(this.value);
scaler(factor);
});
function scaler(factor) {
var newWidth = originalWidth * factor,
newHeight = originalHeight * factor;
$wrap.width(newWidth); $wrap.height(newHeight);
if (factor > 1) {
$wrap.css({ left: 0, top: 0, transform: 'translate(0,0)' });
$elem.css({ transform: 'scale(' + factor + ')' });
setTimeout(setScroll, 400);
} else {
$elem.css({ transform: 'scale(' + factor + ') ' });
$wrap.css({ left: '50%', top: '50%', transform: 'translate(-50%, -50%)' });
}
}
function setScroll() {
var horizontal, vertical;
horizontal = ($wrap.width() - $container.width()) / 2;
vertical = ($wrap.height() - $container.height()) / 2;
$container.stop().animate({scrollTop: vertical, scrollLeft: horizontal}, 500);
}
* { box-sizing: border-box; }
#container {
position: relative;
border: 1px solid red; background-color: blue;
width: 400px; height: 300px;
overflow: scroll;
}
.wrapper {
position: absolute;
background-color: transparent; border: 2px solid black;
width: 300px; height: 200px;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
transition: all 0.5s;
}
.box {
width: 300px; height: 200px;
background: linear-gradient(to right, red, white);
border: 2px solid yellow;
transform-origin: top left;
transition: all 0.5s;
}
.inner { width:50px; height: 50px; background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<label for="slider1">Scale: </label>
<input id="slider1" type="range" min="0.5" max="3" value="1" step="0.25" list="datalist" onchange="scaleValue.value=value" />
<output for="slider1" id="scaleValue">1</output>
<datalist id="datalist">
<option>0.5</option>
<option>0.75</option>
<option>1</option>
<option>1.5</option>
<option>2</option>
<option>3</option>
</datalist>
<hr>
<div id="container">
<div id="wrapper" class="wrapper">
<div id="box" class="box">
<div class="inner"></div>
</div>
</div>
</div>
All scripts tested with IE-11, GC-43, and FF-38
.
The hard part of this problem is that, in the middle of the zoom in process, you need to change from a model (where you have still margins) to another model where you need to expand the size of the container.
Since this happens in the middle of the transform, it's difficult to handle with a single property transform
The way that I have found to solve this is to change the min-height and min-width properties. This will give the posibility to auto detect this point and handle it gracefully
I am keeping in JS only the basic functionality, everything else is done in CSS
function setZoom3() {
var ele = document.getElementById("base");
ele.className = "zoom3";
}
function setZoom1() {
var ele = document.getElementById("base");
ele.className = "";
}
function setZoom05() {
var ele = document.getElementById("base");
ele.className = "zoom05";
}
.container {
width: 300px;
height: 300px;
overflow: scroll;
position: relative;
}
#base {
width: 300px;
height: 300px;
top: 0px;
left: 0px;
position: absolute;
min-width: 300px;
min-height: 300px;
transition: min-width 5s, min-height 5s;
}
.inner {
width: 200px;
height: 200px;
background-color: lightgreen;
background-image: linear-gradient(-45deg, red 5%, yellow 5%, green 95%, blue 95%);
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
transform-origin: top left;
transition: transform 5s;
}
#base.zoom3 {
min-width: 600px;
min-height: 600px;
}
.zoom3 .inner {
transform: scale(3) translate(-50%, -50%);
}
.zoom05 .inner {
transform: scale(0.5) translate(-50%, -50%);
}
.trick {
width: 20px;
height: 20px;
background-color: red;
position: absolute;
transform: translate(0px);
}
<div class="container">
<div id="base">
<div class="inner"></div>
</div>
</div>
<button onclick="setZoom3();">zoom 3</button>
<button onclick="setZoom1();">zoom 1</button>
<button onclick="setZoom05();">zoom 0.5</button>
The only issue left would be to set the scroll position, just in case you want it to keep in the center
I've managed to solve it with a touch of logic. I've used GSAP for the animation and updates, but you could easily get it working with vanilla JS:
http://codepen.io/theprojectsomething/pen/JdZWLV
... Because of the need to scroll the parent div to keep the element centred (and no way to animate the scroll) you're not going to be able to get a smooth transition with CSS alone.
Note: Even without the scroll, a pure CSS transition seems to have trouble syncing (the offset, whether top/left, margin, or translate, is always catching up with the scale) ... this may be due to sub-pixel positioning? Someone else may be able to provide further insight here.
Full code from Codepen:
var $inner = document.querySelector('aside'),
$outer = document.querySelector('main'),
$anim = document.querySelector('[type="checkbox"]'),
$range = document.querySelector('[type="range"]'),
data = {
width: $outer.clientWidth,
value: 1
};
$range.addEventListener('input', slide, false);
function slide () {
$anim.checked ? animate(this.value) : transform(this.value);
}
function animate (value) {
TweenLite.to(data, 0.4, {
value: value,
onUpdate: transform
});
}
function transform (value) {
if( !isNaN(value) ) data.value = value;
var val = Math.sqrt(data.value),
offset = Math.max(1 - val, 0)*0.5,
scroll = (val - 1)*data.width*0.5;
TweenLite.set($inner, {
scale: val,
x: offset*100 + "%",
y: offset*100 + "%"
});
TweenLite.set($outer, {
scrollLeft: scroll,
scrollTop: scroll
});
}
window.onresize = function (){
data.width = $outer.clientWidth;
transform(data.value);
};
main, aside:before, footer {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
main {
width: 50vh;
height: 50vh;
min-width: 190px;
min-height: 190px;
background: black;
overflow: scroll;
box-sizing: border-box;
}
aside {
position: relative;
width: 100%;
height: 100%;
border: 1vh solid rgba(0, 0, 0, 0.5);
background-image: -webkit-linear-gradient(top, yellow, red);
background-image: linear-gradient(to bottom, yellow, red);
box-sizing: border-box;
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
aside:before {
content: "";
background: black;
border-radius: 50%;
width: 10%;
height: 10%;
display: block;
opacity: 0.5;
}
input {
display: block;
margin: 3em auto;
}
input:after {
content: attr(name);
color: white;
text-transform: uppercase;
position: relative;
left: 2em;
display: block;
line-height: 0.9em;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script>
<main>
<aside></aside>
</main>
<footer>
<input type="checkbox" name="animate" checked>
<input type="range" min="0.1" step="0.005" max="3">
</footer>
Matthew King's fiddle was close to being correct.The only thing that needs fixing is the div's offset. If your inner div is exceeding the boundaries of the scrollable div (negative offset) you need to set the offset to 0. Else you want the div to be centered. Centering is however not that trivial. You are zooming the div's background with css, hence you are not affecting the actual width and height of your div and need to parse the scale matrix to calculate it's backgrounds dimension.
This solution works for me: https://jsfiddle.net/6e2g6vzt/11/ (at least for FF on Ubuntu, did not test on other browsers yet)It is basically just one function to set the new offset, you don't even have to call it manually due to this line of code
$("#centered").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", setNewOffset);
which calls the setNewOffset function after the transformation is completed.
As you can see the image 'jumps' to it's correct position after the transformation, maybe you want to add a smooth effect to cover that but i just wanted to show you how to get the correct offset.
Have a look at jQuery's documentation to learn more about .offset()
Credits:
Thanks to Jim Jeffers for the nice callback after transistions https://stackoverflow.com/a/9255507/3586288
Thanks to Lea Verou for the great regex to parse the scale matrix https://stackoverflow.com/a/5604199/3586288

Categories

Resources