How to reserve space for responsive img (stop blocks jumping down) - html

How to solve a problem of jumping down blocks after image load if image should be responsive?
Codesandbox.
Info:
Image should shrink on window resize
Image max-width is it's own width
We don't know image size
JS can be used
Idea:
If it's not possible without any image size data, then can it be done only with image aspect ratio?
If you know image aspect ratio, you can calculate block height and put responsive image inside with some unused space (at least unused space will be not big on small window size).
Image loading:
Image loaded:
Html:
import React from "react";
import "./styles.css";
const img =
"https://s3.amazonaws.com/images.seroundtable.com/google-css-images-1515761601.jpg";
export default function App() {
return (
<div className="app">
<img alt="" src={img + `?${Date.now()}`} className="img" />
<h1>I always jump down :(</h1>
</div>
);
}
CSS:
.app {
box-sizing: border-box;
}
.img {
max-width: 100%;
}
h1 {
background-color: red;
}

AS #DBS sad, it seems that only solution is to wrap image in div block with calculated height based on window.width and aspect ratio (in my case i know it). If there is any better solutions, don't hesitate to answer.
Sandbox solution example.
import React from "react";
import "./styles.css";
import styled from "styled-components";
const aspectRatio = [1, 5];
const img =
"https://s3.amazonaws.com/images.seroundtable.com/google-css-images-1515761601.jpg";
export default function App() {
return (
<div className="app">
<StyledDiv>
<img alt="" src={img + `?${Date.now()}`} className="img" />
</StyledDiv>
<h1>I always jump down :(</h1>
</div>
);
}
const StyledDiv = styled.div`
height: calc(100vw / ${aspectRatio[1]});
overflow-y: hidden;
`;

As you've already determined, even when knowing the aspect ratio, we still have the problem of not knowing the width of the image, making the aspect ratio useless if we are to conform to your requirement Image max-width is it's own width.
What would work is a build time solution (if you know your image urls at build time). There are plenty of server side rendering options for react apps and I will not elaborate on them.
Instead there's a super rudimentary DIY approach.
Create a Node script to run on your server. It will build JSON file with image dimensions. Let's call it "buildImageDimensionData.js". It will output a JSON file imageDimensions.json.
var probe = require("probe-image-size"); // For getting image dimensions without downloading full image
const fs = require("fs"); // For writing our image dimensions to file
// Supply the image urls by hand, or build a webpack script to extract them etc...
const imageUrls = [
`http://localhost:5000/image1.jpg`,
`http://localhost:5000/image2.jpg`,
];
async function getImageDimensionsByUrl(urlList) {
const probePromises = urlList.map(async (imageUrl) => await probe(imageUrl));
let probedImagesData = [];
try {
probedImagesData = await Promise.all(probePromises);
} catch (error) {
// We are programming gods and there will be no error.
}
return probedImagesData.reduce((accumulator, imageData) => {
accumulator[imageData.url] = [imageData.width, imageData.height];
return accumulator;
}, {});
}
getImageDimensionsByUrl(imageUrls).then((dimensions) =>
fs.writeFileSync("imageDimensions.json", JSON.stringify(dimensions))
);
Run the script before build. For example, if you're using Create React App, in package.json:
...
"start": "buildImageDimensionData && react-scripts start",
"build": "buildImageDimensionData && react-scripts build",
...
In your react image component, import imageDimensions.json and use the data to set dimensions for image wrapper based on url of the image.
Note this is more for demonstraing how it could work and there will definitely be packages for doing this better.
Another, albeit poor option is of course just waiting for the images to load before rendering your other content so no jumps occur.

To ignore the jump down, you have to set margin of the next sibling to 0,
.app {
box-sizing: border-box;
}
.img {
max-width: 100%;
}
.app :nth-child(2) {
margin:0px
}

Related

Make absolutely positioned element [ rendered using React ] snap to edge of parent div on page load?

I am making a two-thumb slider bar component in React, where each thumb snaps to the closest of discrete ticks so a user can visually pick from values along a number line. The problem is, as it stands the thumbs need to have position: absolute, but their parent div slots into a regular responsive layout, so the thumbs don't know ahead of time where they're going to need to be on the page - they need to get that information somehow from the parent div itself, on page load. I tried getting my useRef() to the parent div and setting the thumbs' positions within a useEffect(() => {},[]), but apparently that's not possible. How can I tell a child, the position of its parent, immediately on page load in React?
I came up with a hacky solution, that works: I pass the child component a the ref to the parent, and have it set its own position state repeatedly every 10ms [ so from here I have the choice I guess between toning it down to something less embarrassingly overblown but jankier-looking or finding an entirely different solution! ] from within this useInterval hook, so that when the ref to the parent becomes defined, the child snaps there immediately. There are still other bugs, but here is the working Thumb component:
import React, { useEffect, useState, useRef } from 'react';
import { useInterval } from '../utils';
function Thumb(props) {
const { thumb_key,
snap_tick,
bar_ref,
thumb_ref,
color,
n_ticks,
thumb_on_mouse_down } = props;
const [pos,set_pos] = useState(0);
let my_width;
let bar_start;
let bar_width;
useInterval(() => {
my_width = thumb_ref.current.getBoundingClientRect().width;
bar_start = bar_ref.current.getBoundingClientRect().left;
bar_width = bar_ref.current.getBoundingClientRect().width;
set_pos( ( bar_start + ( snap_tick * bar_width ) / (n_ticks - 1) - Math.floor(my_width / 2) ) );
},10);
return (
<div className="thumb-outer"
ref={thumb_ref}
style={{
height: '20px',
width: '20px',
borderRadius: '50%',
backgroundColor: color,
position: 'absolute',
left: pos + 'px',
cursor: 'grab',
dataKey: thumb_key,
}}
onMouseDown={e => thumb_on_mouse_down(e, thumb_key)}
>
</div>
)
};
export default Thumb;

How do I change the contents of webview component to be vertically aligned using writing-mode in nativescript?

Is there any ways that can use css or style to make text contents vertically aligned when working with webview component in nativescript?
I have tried to use class to include a self-made css style, but nothing worked.
.myClass {
writing-mode:vertical-rl
}
I expect there is a way to be able to change the orientation of texts of webpage in the webview component.
As #Manoj mentioned in other post, you can inject CSS after the webpage is loaded.
export function onWebViewLoadFinished(args: EventData) {
const that = this,
webView = <WebView>args.object,
jsStr = `var parent = document.getElementsByTagName('head').item(0);
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = "body {writing-mode: vertical-rl !important;}";
parent.appendChild(style)`;
if (webView.ios) {
webView.ios.evaluateJavaScriptCompletionHandler(jsStr,
function (
result,
error
) {
if (error) {
console.log("error...");
}
});
} else if (webView.android) {
// Works only on Android 19 and above
webView.android.evaluateJavascript(
jsStr,
null
);
}
}
P.S. as per MDN documentation, When set for an entire document, it should be set on the root element (html element for HTML documents).

Error on bodymovin animation in react native?

I am using sample.json image file which is for bodymovin animation in my page through Lottie for React Native.
I am getting the image but the image is not fully retrieved, some parts of the image is missing and also in some side of the image, green color pasted on the image.
But i checked the sample.json through online json image viewr. But there is no issue with the image from the source
here is issue https://i.stack.imgur.com/yFZfg.jpg
here is original image https://i.stack.imgur.com/4sBzg.jpg
so here is my code
import React from 'react';
import { Animated, Easing, easing } from 'react-native';
import Animation from 'lottie-react-native';
export default class BasicExample extends React.Component {
constructor(props) {
super(props);
this.state = {
progress: new Animated.Value(0.5),
};
}
componentDidMount() {
this.startAnimation();
}
startAnimation() {
Animated.timing(
this.state.progress,
{
toValue: 1,
from: 0,
to: 1,
duration: 5000,
}
)
.start(() => {
// Restart at end
this.state.progress.setValue(0);
this.startAnimation();
});
}
render() {
const easing = Easing.inOut(Easing.quad);
const { Animate } = this.props;
return (
<Animation
style={{
width: 300,
height: 300,
}}
source={this.props.Animate}
progress={this.state.progress}
/>
);
}
}
i installed lottie npm also.
so this is my issue please help me to overcome this
Thanks in advance
UPDATE: Now that I looked your code closer I found out that you're animating by changing the value of progress prop. That's not how to do it. You need to use ref for referring the animation to.
return (
<Animation
ref={(animation) => this.myAnimation = animation}
style={{
width: 300,
height: 300,
}}
source={this.props.Animate}
/>
);
And then:
componentDidMount() {
this.startAnimation();
}
startAnimation() {
this.myAnimation.play();
}
OLD ANSWER:
Your code seems perfectly legit and if you see an image, it proofs that you're doing it right.
I'd assume there's either something wrong with the JSON or then Lottie just interprets values wrong.
I've encountered small styling issues on Android devices, but not with iOS. And they're mostly related to alignment.
If you don't get any proper answers here in SO, I'd suggest you to file an issue to github (see this for instance: https://github.com/airbnb/lottie-react-native/issues/182)

Enable pinch to zoom inside iframe - Ionic 2 AngularJS 2

I added zooming="true" inside the tag but when the page is loaded I cannot zoom to increase or decrease the view. I've also set webkitallowfullscreen mozallowfullscreen allowfullscreen to scale the page in order to fit the device screen but nothing changed and the page is still cut.
To explain this concept a little better I take for example Android native apps. Now, if you want to load a page from the web you use a WebView and the result is exactly like using an iframe on Ionic. But on android things become simpler regarding customization:
webview.getSettings().setBuiltInZoomControls(true);
to enable pinch-to-zoom, and
webview.getSettings().setUseWideViewPort(true);
to fit and scale the web page depending on the size of the (mobile) screen.
Now, using Windows 10 it's not possible for me to build native iOS apps so I have to rely on cross-platform development.
Here's my detail-page:
html:
<ion-content>
<iframe sandbox class="link" frameborder="0" [src]="webPage()" zooming="true" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</ion-content>
scss:
detail-page {
.scroll-content{
padding: 0px ;
}
::-webkit-scrollbar,
*::-webkit-scrollbar {
display: none;
}
iframe.link {
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
}
}
ts:
webPage() {
return this.sanitizer.bypassSecurityTrustResourceUrl(this.entry.getElementsByTagName('link')[0].textContent);
}
Hope you can help me.
Edit
I added document.getElementsByTagName('iframe').contentWindow.document.body.style = 'zoom:50%'; but I'm getting a Typescript error:
Typescript Error
Property 'contentWindow' does not exist on type 'NodeListOf<HTMLIFrameElement>'.
Here's my whole .ts file:
export class DetailPage {
entry:any = [];
constructor(private sanitizer: DomSanitizer, public nav: NavController, navParams:NavParams) {
console.log('run');
this.nav = nav;
this.entry = navParams.get('selectedEntry');
console.log('My entry is: "'+ this.entry.getElementsByTagName('title')[0].textContent + '"');
document.getElementsByTagName('iframe').contentWindow.document.body.style = 'zoom:50%';
}
webPage() {
return this.sanitizer.bypassSecurityTrustResourceUrl(this.entry.getElementsByTagName('link')[0].textContent);
}
}
Edit 2
After adding id="myframe" inside <iframe> I've also tried with the function ngAfterViewInit() but still no changes there.
ngAfterViewInit() {
var x = document.getElementById("myframe");
var y = (<HTMLIFrameElement> x).contentWindow.document;
y.body.style.zoom = "50%";
}
And in this form too:
ngAfterViewInit() {
var iframe:HTMLIFrameElement = <HTMLIFrameElement>document.getElementById('myframe');
var iWindow = (<HTMLIFrameElement>iframe).contentWindow.document;
iWindow.body.style.zoom = "50%";
}
I think it is not possible to do in a IFrame as that will be a security flaw.
what you are basically doing is trying to access a webpage from your mobile hybrid app (Ionic App in your case).
it must not allow you to run javascript on that webpage, workaround for it will be by disabling web security on that browser or in your case webview (not sure how to do that in mobile but that is manual browser customization so will not work in your scenario).
more explanation on this post
SecurityError: Blocked a frame with origin from accessing a cross-origin frame
You will need to track the gesture and apply the change of zoom to the iframe like this
document.getElementByTagName('iframe').contentWindow.document.body.style = 'zoom:50%';
Here the zoom is set to 50%, but this can be added dynamically using the gesture event values.

Prevent images from being downloaded to page on mobile site

How can I make it so that within the mobile version of my site the images are not downloaded to from the web server as these are large files that are not needed and not being used and therefore severely impacting the use of the mobile version of the site. Having looking at previous threads of such nature I saw that hiding the parent of the image using code such as below can benefit.
.parent {display:block;}
.background {background-image:url(myimage.png);}
#media only screen and (max-width:480px) {
.parent {display:none;}
}
The problem being I don't want to use background image CSS for SEO issues associated with them as I like to use Schema tagging etc ..so how can I prevent an IMG tag from being downloaded, as display:none; only hides the image rather than stopping it being downloaded.
Note: This is not for copyright protection issues e.g. preventing right click etc etc but for speed and ultimately size of the downloaded content to mobile.
This solution uses CSS to prevent background-images from loading and jQuery to prevent images from loading. I'm not familiar with any CSS solution that will prevent images from loading.
JS Fiddle: http://jsfiddle.net/CoryDanielson/rLKuE/6/
If you know the images height and width (or even ratio) ahead of time you could set the background-image for a bunch of fixed size DIVs. This might be applicable for icons and layout-type images. Look at the HTML/CSS below for an example of that.
Background Images
/* hidden by default */
aside {
display: none;
}
/* Pictures load for 'big screen' users.. pcs/tablets? */
#media screen and (min-width: 750px) {
aside {
display: block;
}
.catpicDiv {
height: 100px;
width: 100px;
display: inline-block;
border: 1px solid red;
background-image: url('http://img2.timeinc.net/health/images/slides/poodle-1-400x400.jpg');
background-size: cover;
}
}
and HTML
<aside>
<div class="catpicDiv"></div>
<div class="catpicDiv"></div>
<div class="catpicDiv"></div>
</aside>
Image Elements are a different story...
I don't know of any purely CSS solution to prevent them from loading the images. So I'd solve it like this:
Define IMG tags as follows
<img src="" data-src="url-to-image.jpg" />
Then, somewhere in the head of the document you need similar javascript
1) Function to load all of the images
function loadAllTheImages() {
$("img").each(function(){
$(this).attr('src', $(this).attr('data-src'));
});
}
2) Code to determine if the user is on mobile or a PC (slow vs fast connection) and then load the images.
This code isn't bulletproof, there are much more accurate and reasonable tests than this.
$(window).load(function(){
if ( $(window).width() > 750 ) {
loadAllTheImages(); // !
} else {
$("body").append("<a id='mobileCheck' href='javascript: void(0);'>I GOTS 4G, LEMME HAVE EM!</a>");
}
});
3) As well as maybe some code to activate a button to load the images anyways? Why not, I guess... ?
$(document).ready(function(){
$('body').prepend("<h1>" + $(window).width().toString() + "</h1>");
$('body').on('click', '#mobileCheck', function(){
loadAllTheImages(); // !
$("#mobileCheck").remove();
});
});
Similar solution as here and what I hypothesized in the comments:
Delay image loading with jQuery
There is no native solution in CSS that would prevent images from loading even if you hide them or set display to none.
You have to use some JS to achieve that result. If you are familiar with JS that should not be an issue at all. There are several plugins ready to go to do what you want. You can also write your own JS because its not that difficult.
Here is my code that loads images based on the screen size:
DEMO AT CODE PEN
It works without any libraries like JQ but if you use one of those it will automatically switch to it (Tweak it to your specific needs).
JS
// use jQuery or pure JS
if (typeof jQuery !== 'undefined') {
// jQuery way
// alert("jquery");
$(function() {
$(window).on('load resize', function() {
var products = $("[data-product-image]");
products.each(function(key, value) {
var bg = null;
if (window.outerWidth < 500) return;
if (window.outerWidth < 1000) bg = $(value).data("product-image-s");
if (window.outerWidth >= 1000) bg = $(value).data("product-image");
console.log($(window).outerWidth);
$(value).css({
'background-image': 'url(' + bg + ')',
'background-position': 'center',
'background-size': 'cover',
});
});
});
});
} else {
// Pure JS way
// alert("JS");
(function() {
window.addEventListener('load', wlImageLoader);
window.addEventListener('resize', wlImageLoader);
function wlImageLoader() {
console.log('event! Trig trig');
var all = document.getElementsByTagName("div");
var products = [];
for (i = 0; i < all.length; i++) {
if (all[i].hasAttribute('data-product-image')) {
products.push(all[i]);
}
}
Array.prototype.forEach.call(products, function(value) {
var bg = null;
var curent = window.getComputedStyle(value).getPropertyValue('background-image');
console.log(curent);
if (window.outerWidth < 500 || curent != 'none') return;
if (window.outerWidth < 1000 && curent == 'none') bg = value.getAttribute('data-product-image-s');
if (window.outerWidth >= 1000 && curent == 'none') bg = value.getAttribute('data-product-image');
// if (window.outerWidth >= 2000 && curent == null) bg = value.getAttribute('data-product-image-l');
if(bg == null || curent != 'none') return;
value.style.backgroundImage = "url(" + bg + ")";
value.style.backgroundPosition = "center";
value.style.backgroundSize = "cover";
curent = window.getComputedStyle(value).getPropertyValue('background-image');
console.log(curent);
});
}
})();
}
HTML
<div data-product-image="img/something_normal.jpg" data-product-image-s="img/something_small.jpg" id="p3" class="product">
However if you are a time loading freak you probably prefer to write your code natively in JS as you often don't use most of the jQuery library. For fast internet connection this is not a problem but if you target mobile devices on country side that might make a difference.
I would suggest combining perhaps the #import and #media commands to only #import the stylesheet which contains images if the #media tag meets you criteria (say, over a certain resolution).
So by default you wouldn't import the stylesheet which applies the BG image, you'd only end up doing it if you had determined the site was 'non-mobile'..if that makes sense!
The W3c site has some decent examples of combining the rules:
http://www.w3.org/TR/css3-mediaqueries/#media0