div scale transform problems - javascript

I am using a scale transform on a div so that I can use child elements with set positions and sizes and still scale to fill the screen. I am writing in GWT 2.7 and am having problems changing element visibility in Chrome and Safari (webkit bug?) on a Mac. Here's an example:
<!doctype html>
<html lang="en">
<head>
<style>
#container { position: absolute; left: 50px; top: 30px; width: 576px; height: 456px; overflow: hidden; background-color: cyan; }
#backCanvas { position: absolute; left: 44px; top: 260px; }
#frontCanvas { position: absolute; left: 240px; top: 397px; }
.scaled
{
-ms-transform-origin: 0 0; /* IE 9 */
-ms-transform: scale(1.2);
-webkit-transform-origin: 0 0; /* Safari 8 */
-webkit-transform: scale(1.2);
transform-origin: 0 0;
transform: scale(1.2);
}
</style>
</head>
<body onload="initCanvas()">
<div id="container" class="">
<canvas id="backCanvas" tabindex="-1" width="490" height="158"></canvas>
<canvas id="frontCanvas" tabindex="-1" width="87" height="31"></canvas>
<button onclick="showScaled(false)">Unscaled</button>
<button onclick="showScaled(true)">Scale transform</button>
<button onclick="showElement('frontCanvas',false)">Hide canvas</button>
<button onclick="showElement('frontCanvas',true)">Show canvas</button>
</div>
<script>
function initCanvas()
{
fillCanvas(document.getElementById("backCanvas"), "green");
fillCanvas(document.getElementById("frontCanvas"), "red");
}
function fillCanvas(canvas, color)
{
var context = canvas.getContext("2d");
context.fillStyle = color;
context.fillRect(0, 0, canvas.width, canvas.height);
}
function showScaled(scaled)
{
document.getElementById("container").className = scaled ? "scaled" : "";
}
function showElement(element, show)
{
// none of these work when scaled
document.getElementById(element).style.display = show ? null : "none";
// document.getElementById(element).style.visibility = show ? "visible" : "hidden";
// document.getElementById(element).style.zIndex = show ? "100" : "-100";
// document.getElementById(element).style.left = show ? "240px" : "-1000px";
}
</script>
</body>
</html>

The problem has to do with overflow:hidden. I included that in the container because that's what GWT does. So, I removed overflow from the container and added overflow:hidden to the body. This works for my app because I scale to fit the window. There may be some clipping problems with the container, but now the draw order works correctly.
This is a webKit bug that is in the process of being fixed.

Related

Setting an element position according to another element (seems JS/CSS bug)

I have a div that will be set according to the hovered element position in window. At first I thought this was a JQuery bug, but after more investigating and changing to vanilla, it's still the same.
I have created a code snippet to demonstrate my problem. If you mouse enter white div from top, the position is correct and orange box cover entire white box, but if you enter it from other sides, it's incorrect by few pixel:
var inspector_rect2= document.getElementById('inspector_rect');
$(window).mouseover(function(event) {
inspector_rect2.style.left= event.target.getBoundingClientRect().x+'px';
inspector_rect2.style.top= event.target.getBoundingClientRect().y+'px';
inspector_rect2.style.width= event.target.getBoundingClientRect().width+'px';
inspector_rect2.style.height= event.target.getBoundingClientRect().height+'px';
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
</head>
<style>
html, body {
height : 100%;
margin : 0;
width : 100%;
}
.MyCSS {
background-color : silver;
}
.Container {
height : 100%;
margin : auto;
width : 50%;
}
.Header {
height : 5%;
padding : 2% 0;
width : 100%;
}
.MainContent {
background-color : white;
height : 70%;
width: 100%;
}
.inspector{
position: absolute;
pointer-events: none;
z-index: 999;
background: rgba(255, 166, 0, 0.5);
}
</style>
<body class="MyCSS">
<div class="Container" >
<div class="Header" ></div>
<div class="MainContent" ></div>
</div>
</body>
<div id=inspector_rect class=inspector></div>
It seems to be caused by an interaction between requesting .getBoundingClientRect() and setting width and height.
Generally, you should just make one request, store it, then re-use as needed.
$(window).mouseover(function(event) {
const rect = event.target.getBoundingClientRect();
inspector_rect2.style.left= rect.x+'px';
inspector_rect2.style.top= rect.y+'px';
inspector_rect2.style.width= rect.width+'px';
inspector_rect2.style.height= rect.height+'px';
});

How do I make my flipping image display a different image on either side with javascript?

I'm trying to make the different sides of the object display different images; however, any similar question I've seen has an incredibly overcomplicated answer. I'm not too fluent in js just yet but I'd appreciate the help.
<script>
var k = 0;
function flip() {
var j = document.getElementById("card");
k += 720;
j.style.transform = "rotatey(" + k + "deg)";
j.style.transitionDuration = "7s"
}
</script>
<div id="card" onmouseover="flip()"><img src="day.png"></div>
That is my html and here is my CSS:
#card {
display: block;
width: 300px;
height: 300px;
padding: 25px;
}
Let me know if any clarification is needed, thanks for the help!
Edit: I currently have an object that rotates along the y-axis when moused over. I would like there to be another image displayed after the initial image has rotated 90 degrees (displaying from 90-270 degrees, and then switching back to the previous image from 270-450 degrees, and so on). Not sure why the post was downvoted, but I hope this is the ludicrous specificity they were looking for.
You can actually do this with CSS and HTML alone.
.card-container {
perspective: 1000px;
}
.card-container:hover .card {
transform: rotateY(180deg);
}
.card-container, .front, .back {
width: 200px;
height: 200px;
}
.front, .back {
background: #efe;
}
.card {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
}
.front, .back {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
}
.front {
z-index: 2;
transform: rotateY(0deg);
}
.back {
transform: rotateY(180deg);
}
<div class="card-container">
<div class="card">
<div class="front">
Front
</div>
<div class="back">
Back
</div>
</div>
</div>
I'm new to Javascript too and I don't know if this is the best solution to use in a production environment but here's what I came up with.
Basically, you can rotate the image 180 degrees and change the image source attribute halfway. So, if you set your rotation to complete in 1 second, you can use setTimeout with a delay of around half a second to call a function that will change the image source.
var k = 0;
var imgSrc1 = "https://static.pexels.com/photos/9291/nature-bird-flying-red.jpg";
var imgSrc2 = "http://media-channel.nationalgeographic.com/media/uploads/photos/content/video/2014/10/29/349582915975_349582915975_720p_5994_Racing_Speeds_DMS.jpg";
var img = document.getElementById("img");
img.onmouseover = flip;
img.style.transitionDuration = "1s";
function flip() {
k += 180;
img.style.transform = "rotatey(" + k + "deg)";
setTimeout(changeImgSrc(), 300);
}
function changeImgSrc() {
return function() {
if(img.getAttribute("src") === imgSrc1)
img.src = imgSrc2;
else
img.src = imgSrc1;
}
}
img { width:150px; height: 150px;}
<img id="img" src="https://static.pexels.com/photos/9291/nature-bird-flying-red.jpg">

CSS imageviewer: Image rotation (and scaling)

I'm trying to have a simplistic image viewer in a react project of mine.
Is there a way to implement rotation (and ideally scaling) without referencing the current dimensions of the DOM node in JS?
So far I tried something like this (reduced case/taken from my jsfiddle):
function getTransform() {
switch (rotation) {
case 90: return {
transform: 'translateY(-100%) rotate(90deg)',
'transform-origin': 'bottom left'
};
case 180: return {
transform: 'translate(100%, -100%) rotate(180deg)',
'transform-origin': 'bottom left'
}
case 270: return {
transform: 'translateX(-100%) rotate(270deg)',
'transform-origin': 'top right'
};
default: return {
transform: 'none'
}
}
}
and apply that as styles on my element. Unfortunately that doesn't work. While I can use the 100%/relative values for Y (i.e. 90 degrees works, 180 degrees works for the Y axis at least), I cannot use it to translate the X axis - parts of the image are offscreen.
Demo: https://jsfiddle.net/7huLa8e1/2/
Is there a way to use relative values alone or do I have to grab the DOM node and use width/height in absolute pixel values to make this work?
If I understand your question correctly, your problem is that the image overflows the container to the right in an unspecified size.
We need to get around this. My idea is to float right the image inside the container. we will need a clearfix to keep it taking the correct size.
I have redone the jquery to works with css classes, since it is the easiest way to modify the child (the image)
var rotation = 0;
function rotate(deg) {
rotation = (360 + rotation + deg) % 360;
$('#image-viewer').attr('class', 'rotate' + rotation);
}
$(function(){
$('#rotateLeft').click(
function(event) {
rotate(-90);
}
);
$('#rotateRight').click(
function(event) {
rotate(90);
}
);
});
#image-viewer {
position: relative;
cursor: pointer;
border: solid red 2px;
}
.image-viewer-root {
height: 100vh;
}
.image-viewer-viewport {
height: 90%;
overflow: auto;
}
.rotate90 {
transform: rotate(90deg) translate(0%, -100%);
transform-origin: left top;
}
.rotate180 {
transform: rotate(180deg);
transform-origin: center center;
}
.rotate180 img {
float: right;
}
.rotate270 {
transform: translate(-100%, 0%) rotate(270deg);
transform-origin: right top;
}
.rotate270 img {
float: right;
}
.clearfix {
clear: both;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="image-viewer-root">
<nav class="toolbar navbar navbar-dark bg-faded">
<div class="nav navbar-nav">
<button id="rotateLeft" class="btn nav-item">RotateLeft</button>
<button id="rotateRight" class="btn nav-item">RotateRight</button>
</div>
</nav>
<div class="image-viewer-viewport">
<div id="image-viewer">
<img src="https://sstatic.net/stackexchange/img/logos/so/so-logo.png?v=9c558ec15d8a">
<div class="clearfix"></div>
</div>
</div>
</div>

How do I scale down a div within another div using CSS?

I'm trying to scale down a div (divB) that's inside of another div (divA). The problem is that divA's height is specified by its contents. When divB gets scaled down, the height of divA doesn't change... This happens when a transform is applied because it doesn't actually change the pixel count, it changes the size of the pixels themselves (at least I'm pretty sure that's what's happening). So the easy fix is to manually set the height of divA to be the size of divB multiplied by the scale factor.
However, if I do this, I need to reset the height manually every time the contents of divA change. In my case, this is very cumbersome as there will be a ton of changes to divA's contents. So, I'm wondering if there is a simpler way to do this, preferably using CSS.
Here is a simple JSFiddle to demonstrate the problem: http://jsfiddle.net/turtlewaxer1100/82cux/8/
Just add some elements, scale down, and you'll see what I mean about the height not adjusting. If you click "Fix Height" then it'll adjust the height properly, but then if you add more elements the height doesn't adjust unless you fix it again...
html
<div>
<div class="wrapper">
<div id="scalar"></div>
</div>
<div class="buttons">
<div id="button">
<input type="button" value="Add" />
</div>
<div id="scaleDown">
<input type="button" value="Scale Down" />
</div>
<div id="scaleUp">
<input type="button" value="Scale Up" />
</div>
<div id="fixHeight">
<input type="button" value="Fix Height" />
</div>
</div>
</div>
css
.wrapper {
float:right;
width: 200px;
background-color: black;
}
.section {
margin-left:75px;
height: 50px;
width: 50px;
background-color: red;
}
.buttons {
float:left;
}
.scaleDown {
-webkit-transform: scale(0.75);
-moz-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 0 0;
-moz-transform-origin: 50% 0 0;
-ms-transform-origin: 50% 0 0;
transform-origin: 50% 0 0;
}
jquery
var section = $("<div class='section'></div>")
$(document).ready(function () {
$("#button").children().click(function () {
$("#scalar").append(section.clone(false));
});
$("#scaleDown").children().click(function () {
$("#scalar").addClass("scaleDown");
});
$("#scaleUp").children().click(function () {
$("#scalar").removeClass("scaleDown");
});
$("#fixHeight").children().click(function () {
$(".wrapper").height($("#scalar").height()*.75)
});
});
So I couldn't find an answer using just CSS, but I did find a fairly simple javascript solution. There is a DOM event called "DOMSubtreeModified", which will fire every time any element within the current element's hierarchy is changed. So, what I did is test whether the height of the element is different from the previous height each time this event is fired. If it is different, then set it accordingly. This way you can catch all dynamic height changes without any regard to what specifically changed the height.
Here is a fiddle to demonstrate the idea: http://jsfiddle.net/turtlewaxer1100/E6D2H/5/
HTML
<div class="wrapper">
<div id="scalar"></div>
</div>
<div class="buttons">
<div id="button">
<input type="button" value="Add" />
</div>
</div>
CSS
.wrapper {
float:right;
width: 200px;
background-color: black;
}
.section {
margin-left:75px;
height: 50px;
width: 50px;
background-color: red;
}
.buttons {
float:left;
}
#scalar
{
-webkit-transform: scale(0.75);
-moz-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 0 0;
-moz-transform-origin: 50% 0 0;
-ms-transform-origin: 50% 0 0;
transform-origin: 50% 0 0;
}
JS
var section = $("<div class='section'></div>")
var scaleFactor = 0.75;
var originalHeight = 0;
$(document).ready(function () {
$("#button").children().click(function () {
$("#scalar").append(section.clone(false));
});
$(".wrapper").bind('DOMSubtreeModified', function() {
var height, heightOffset;
height = $("#scalar").height();
if (height && originalHeight !== height) {
heightOffset = height * scaleFactor;
$(this).height(heightOffset);
return originalHeight = height;
}
});
});

Center and resize content

I have a user-variable string, which can range from one word to a couple sentences (and might contain any valid Unicode character), which I'd like to display within a variable width box.
In code, I'd like HTML that looks like this w/ any other CSS or JS:
<div style="width: 100%; height: 80%" id="text">
<!--<some more divs or something>-->
{{content}}
<!--</some more divs or something>-->
</div>
{{content}} should get bigger when it can be, up to some maximum font size (variable); smaller when it's longer down to some minimum (variable) and then just get cut off after that point.
In either case, I need it to be visually centered and words longer than the box should get hyphenated.
I've tried hacking something together with a combination of flexboxes and JavaScript, but couldn't figure out how to get all the bugs worked out.
Browser support doesn't really matter aside from the latest versions of mobile/desktop Chrome/Safari/Firefox.
Alright I believe this is what you were wanting to accomplish. Code is below with descriptions in the comment blocks. In chrome you'll be using the -webkit-line-clamp property, in firefox you'll be using the fadeout method since firefox doesn't support the clamp property. You can adjust the fadeout in the css to your liking. The "..." at the cutoff point will also still be present in firefox (see the .clamp:after property in the css).
Here is the updated jsFiddle
HTML (To see the changes, just remove the text until one line is shown in the div)
<div id="textparent">
<div id="text">
{{content}} adkf kfjg; ;akdfg fbfbf egdf hajkh
kajfhdg lakjfg kafd gjkahf jahfkjadlfh alkgj akjdhg fkafg
</div>
</div>
CSS
Note: -webkit-line-clamp:3; ( this is the amount of lines you want to be shown)
#text{
width:100%;
position:relative;
height:auto;
text-overflow:ellipsis;
font-size:25px;
line-height:1.1;
display:block;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp:3;
overflow:hidden;
margin:0 auto;
box-sizing:border-box;
}
#textparent{
margin:0 auto;
width:300px;
background:#eee;
top:50px;
padding:10px;
height:auto;
text-align:center;
position:relative;
height:100px;
display:-webkit-box;
-webkit-box-pack:center;
-webkit-box-align:center;
}
/*FIREFOX will make use of the clamp class*/
.clamp:after {
background: linear-gradient(to right, rgba(255, 255, 255, 0), #eeeeee 50%) repeat scroll 0 0 rgba(0, 0, 0, 0);
bottom: 0;
content: "...";
padding: 0 5px 1px 25px;
position: absolute;
right: 0;
}
.clamp {
height: 5.6em;
line-height: 1.4em;
overflow: hidden;
position: relative;
}
Javascript/JQUERY: The main variable you might want to change or play around with is [min_font_size] and [num_line_to_show] although [num_line_to_show] is already set in the CSS.
var t = $('#text');
// get the font-size of the div
var font_size = Number(t.css('font-size').replace('px', ''));
// get the line-height of the div (Note: in Chrome this returns the actual height)
var line_height = Number(t.css('line-height').replace('px', ''));
// minimum height of #text div
//
// Note: if you were in a browser and the line-height var didn't return the full
// height as it does in chrome, you would need to do this:
// var min_h = font-size * line_height
var min_h = line_height;
// number of lines to show. basically just retrieving the "-webkit-line-clamp"
// property in the css, otherwise will default to 3, which you can change.
var num_line_to_show = Number(t.css('-webkit-line-clamp')) || 3;
// the maximum height for the #text div. (the added 5 at the end is just
// personal preference)
var max_h = line_height * num_line_to_show * font_size + 5;
// get the height of the div
var h = $('#text').height();
// set this if you want the font to be set at a minimum size
// when the text is longer than one line
var min_font_size = 20;
Note: you could also try setting the minimum font size dynamically, something like this:
// change this to make the font smaller
var shrink_rate = 3;
var min_font_size = font_size - (Math.round((h/min_h)) * shrink_rate;
Continuing:
// for detecting firefox
var is_ff = navigator.userAgent.toLowerCase().indexOf('firefox');
// if the height of the div is larger than the minimum height, meaning there
// is more than one line now, the font size of the div becomes smaller.
if (h > min_h){
t.css({'font-size' : min_font_size});
// if in Firefox browser
if(is_ff > -1){
// get the new max height of #text based on the number of lines
// with the new minimum font-size
var txt_max_h = ((line_height-font_size) / num_line_to_show) * min_font_size * num_line_to_show;
// the new height is greater than the maximum height allowed for the
// smaller font size
if (t.height() > txt_max_h){
// reset the height of #text div to a fixed height
t.height((min_font_size * num_line_to_show) + 5);
// add the clamp class and css will the rest
t.addClass('clamp');
}
}
}
// if firefox, always run this to center the #text div based on its height
if(is_ff > -1){
t.css({top: ($('#textparent').height() - t.height()) / 2});
}
Hope this helps!
just in time.
See this Fiddle.
I think I succeed to do what you want. It works with Chrome, Firefox and Safari.
HTML :
<div id="container">
<div id="text">my Text !!</div>
</div>
JS :
var maxFontSize=68; // I think we cannot have bigger than that.
var minFontSize=12;
$('#text').on({
// setting an event to resize text
resize:function(e){
// if no text => return false;
if (!$(this).html().trim()) return;
// if already running => return false;
if (this.running) return;
this.running = true;
// get max-height = height of the parent element
var h = $(this).parent().height();
// clone the text element and apply some css
var clone = $(this).clone()
.removeAttr('id')
.css({'font-size':0,
'width':$(this).width(),
'opacity':0,
'position':'fixed',
'left':-1000})
.appendTo($('body'));
// Set the max font size for the clone to fit the max height;
var fontSize = minFontSize;
do {
$(this).css('font-size', fontSize+'px');
fontSize=fontSize+1;
clone.css('font-size', fontSize+'px');
} while (h > clone.height() && maxFontSize > fontSize) ;
// Set the '...' if still bigger
//start by setting back the good size to the clone.
fontSize=fontSize-1;
clone.css('font-size', fontSize+'px');
// while max-height still bigger than clone height
if (h < clone.height() && minFontSize == fontSize) {
var content = clone.html();
// try to remove the last words, one by one.
while (h < clone.height()) {
content = content.replace(/(\s[^\s]*)$/g,'...');
clone.html(content);
}
// then replace the #text content
$(this).html(clone.html());
}
// then remove the clone
clone.remove();
this.running = false;
}
})
.trigger('resize');
There is a cross-browser (IE9+) css centered text and hyphenated for webkit, codepen:
HTML:
<div class="box">
<p>
You can also position your element only in the vertical or horizontal.
This work in IE9+. This text can be also hyphenated.
</p>
</div>
CSS:
.box {
border: #3071a9 solid 1px;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
color: #222;
font-size: 26px;
font-family: arial;
height: 50%;
padding: 20px;
width: 50%;
}
.box p {
text-overflow:ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp:3;
overflow: hidden;
position: absolute;
top: 50%;
-webkit-transform: translate(0, -50%);
-ms-transform: translate(0, -50%);
-o-transform: translate(0, -50%);
transform: translate(0, -50%);
}
Jquery Textfill Plugin by Russ Painter can come handy.
Here is the Fiddle.
<div>
<div>
<label for="dyntext">Content:</label>
<input type="text" id="dyntext" value="Hello!"></input>
</div>
<div>
<label for="maxsize">Maximal font size in pixels?</label>
<input type="text" id="maxsize" value="0"></input>
</div>
<hr />
<div class="content">
<div class="jtextfill">
<span class="dyntextval">Hello!</span>
</div>
</div>
function update() {
var size = parseInt($('#maxsize').val(), 10);
if (!isNaN(size)) {
$('.dyntextval').html($('#dyntext').val());
$('.jtextfill').textfill({debug: true, maxFontPixels: size});
}
}
$(function () {
$('#maxsize').keyup(update);
$('#dyntext').keyup(update);
update()
});
.content .jtextfill {
width: 150px;
height: 100px;
background-color: #fff;
text-align: center;
border:1px solid #333;
padding-top:40px;
padding-bottom:40px;
}
The center part is really easy, you can do this with flexbox, display:table-cell, etc
The font-size part is tricky but it's been answered in the past here: https://stackoverflow.com/a/6112914/1877754

Categories

Resources