I am new to working on css/html and I was trying to do parallax effect contained in some border radius but every time I try to do it using a background (url) it doesn't seem to do what I want it to, so I was wondering if it'd be possible to do it from the img tag?
You can solve your problem using JS. Check out the example below. It will work for you. Have a nice day.
$('.img-parallax').each(function() {
var $image = $(this);
var $imageParent = $(this).parent();
function parallaxImg () {
var speed = $image.data('speed');
var imageY = $imageParent.offset().top;
var winY = $(this).scrollTop();
var winH = $(this).height();
var parentH = $imageParent.innerHeight();
// The next pixel to show on screen
var winBottom = winY + winH;
// If block is shown on screen
if (winBottom > imageY && winY < imageY + parentH) {
// Number of pixels shown after block appear
var imgBottom = ((winBottom - imageY) * speed);
// Max number of pixels until block disappear
var imgTop = winH + parentH;
// Percentage between start showing until disappearing
var imgPercent = ((imgBottom / imgTop) * 100) + (50 - (speed * 50));
}
$image.css({ top: imgPercent + '%', transform: 'translate(-50%, -' + imgPercent + '%)' });
}
$(document).on({
scroll: function () {
parallaxImg();
}, ready: function () {
parallaxImg();
}
});
});
#import url(https://fonts.googleapis.com/css?family=Amatic+SC:400,700);
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
font-family: "Amatic SC", cursive;
}
.block {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
font-size: 16px;
}
.block h2 {
position: relative;
display: block;
text-align: center;
margin: 0;
top: 50%;
transform: translateY(-50%);
font-size: 10vw;
color: white;
font-weight: 400;
}
.img-parallax {
width: 100vmax;
z-index: -1;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, 0);
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="block">
<img src="https://unsplash.it/1920/1920/?image=1003" data-speed="-1" class="img-parallax">
<h2>Parallax 1</h2>
</div>
<div class="block">
<img src="https://unsplash.it/1920/1920/?image=1002" data-speed="1" class="img-parallax">
<h2>Parallax 2</h2>
</div>
<div class="block">
<img src="https://unsplash.it/1920/1920/?image=1014" data-speed="1" class="img-parallax">
<h2>Parallax 3</h2>
</div>
I have this fiddle.
https://jsfiddle.net/oeuc8L9y/2/
If you click the background the coordinates where you clicked get centered.
Is it possible to make the pointer events only work inside the hole?
Since I'm using an image for the ring the whole thing blocks the pointer-events but if I set it to pointer-events: none; I can click trough the ring too.
This is good.
This is bad.
I assume a way would be to get the pixel coordinate and calculate if it's inside the hole but I feel like that would only work for a specific screen size and it'd break if resized.
tooltip_X = $("#x-coords");
tooltip_Y = $("#y-coords");
$("#background").on("mouseover", (e) => {
showTooltip(e);
});
$("#background").on("mousemove", (e) => {
updateCoords(e);
moveTooltip(e);
});
$("#background").on("mouseout", (e) => {
hideTooltip(e);
});
$("#background").on("click", (e) => {
move(e);
updateCoords(e);
});
function showTooltip(e) {
$("#tooltip").css("display", "block");
}
function hideTooltip(e) {
$("#tooltip").css("display", "none");
}
function moveTooltip(e) {
var left = 0;
var top = 0;
if (e.pageX + $("#tooltip").width() + 10 < document.body.clientWidth) {
left = e.pageX + 10;
} else {
left = document.body.clientWidth + 5 - $("#tooltip").width();
}
if (e.pageY + $("#tooltip").height() + 10 < document.body.clientHeight) {
top = e.pageY + 10;
} else {
top = document.body.clientHeight + 5 - $("#tooltip").height();
}
$("#tooltip").offset({ top: top, left: left });
}
function updateCoords(e) {
var mouse_x = e.clientX - $("#background").offset().left;
var mouse_y = e.clientY - $("#background").offset().top;
$("#x-coords").text(Number.parseInt(mouse_x));
$("#y-coords").text(Number.parseInt(mouse_y));
}
function move(e) {
var mouse_x = e.clientX - $("#background").offset().left;
var mouse_y = e.clientY - $("#background").offset().top;
var new_x = 0;
var new_y = 0;
if (mouse_x < 250) {
mouse_x = 250;
}
if (mouse_y < 250) {
mouse_y = 250;
}
if (mouse_x > 1670) {
mouse_x = 1670;
}
if (mouse_y > 950) {
mouse_y = 950;
}
new_x = -(mouse_x - 250);
new_y = -(mouse_y - 250);
$("#background").css("margin-top", new_y);
$("#background").css("margin-left", new_x);
}
#container {
position: relative;
width: 500px;
height: 500px;
overflow: hidden;
z-index: 100;
}
#ring {
position: absolute;
border-radius: 100%;
max-height: 500px;
max-width: 500px;
z-index: 90;
pointer-events: none;
}
#lens {
position: absolute;
border: 1px solid red;
/*
something to make it round
*/
z-index: 80;
pointer-events: none;
}
#background {
position: absolute;
width: 1920px;
height: 1200px;
background-image: url("https://wallpaperaccess.com/full/197542.jpg");
z-index: 80;
outline: 5px dotted purple;
outline-offset: -5px;
}
#tooltip {
position: absolute;
white-space: nowrap;
background: #ffffcc;
border: 1px solid black;
padding: 5px;
color: black;
z-index: 999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head> </head>
<body>
<div id="container">
<img id="ring" src="https://pngimg.com/uploads/circle/circle_PNG26.png" alt="Lens Ring" />
<div id="lens"></div>
<div id="background"></div>
<span id="tooltip"> Div Coords (<span id="x-coords"></span>,<span id="y-coords"></span>) </span>
</div>
</body>
</html>
(Please ignore the empty squares.)
without CSS view { height: 45em; }, I get: (position overlap)
with CSS view { height: 45em; }, I get: (unwanted, position mismatch)
How can I have the blue <span> element positioned correctly in the second case?
<view style="height: 45em;">
<pdf-page> <!-- position: relative -->
<text class="textLayer"> <!-- position: absolute -->
<span style="left: 417.34px; top: 37.8391px; ..."></span> <!-- position: absolute -->
</text>
<svg width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842" xmlns="http://www.w3.org/2000/svg" version="1.1">
<g ⋯><g ⋯><text><tspan></tspan></text></g></g>
</svg>
</pdf-page>
</view>
Here is the complete case in stackoverflow (see /* ← */ in the second pane after clicking on Show code snippet):
#namespace url(http://www.w3.org/1999/xhtml);
#namespace svg url(http://www.w3.org/2000/svg);
/*pdf.css*/
:root {
--pdf-page-outline-color: #aaa;
--pdf-page-background-color: #fcfcfc;
}
pdf-file { display: contents; }
pdf-page {
display: inline-block;
outline: 1px solid var(--pdf-page-outline-color);
background-color: var(--pdf-page-background-color);
}
pdf-page { position: relative; }
/* text.css */
.textLayer {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
width: 100%; height: 100%;
-overflow: hidden;
opacity: 1;
-line-height: 1;
}
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
/**/
view { background: green; }
.textLayer { background: rgba(0, 255, 0, .1); }
svg|svg { background: rgba(255, 0, 0, .1); }
<style>
view {
height: 45em; /* ← */
display: flex;
overflow: auto;
flex-direction: column;
place-items: center;
scroll-snap-type: y mandatory;
overflow: auto;
}
pdf-page { height: 100%; scroll-snap-align: start; }
svg { height: 100%; width: auto; }
text { overflow: visible; background: rgb(0, 0, 0, .1); }
text > span { background: rgba(0,0,255,.1); }
</style>
<view -onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" svg="">
<text class="textLayer">
<span style="left: 417.34px; top: 37.8391px; font-size: 12px; font-family: sans-serif; transform: scaleX(1.07482);">Plenarprotokoll 16/3</span>
</text>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842">
<g transform="matrix(1 0 0 -1 -8 850)">
<g transform="">
<text transform="matrix(12 0 0 12 425.34 801.2976) scale(1, -1)" xml:space="preserve">
<tspan x="0 0.6672 0.9454 1.5016 2.1128 2.669 3.0582 3.6694 4.0586 4.6698 5.003 5.6142 6.1704 6.7816 7.0598 7.6132 8.1694 8.7256 9.0038" y="0" font-family="g_d0_f1" font-size="1px" fill="rgb(0,0,0)"></tspan>
</text>
</g>
</g>
</svg>
</pdf-page>
</view>
(also available for review on codepen: https://codepen.io/cetinsert/pen/MWeVxLe?editors=1100)
A much more precise way is to just transform: scale(x, y) the <text> layer once on resize without any <span style> position value recalculations / unit change.
This answer has triggered the launch of my commercial project.
– https://WebPDF.pro
Zero-dependency, truly HTML-native PDF web components.
const t = document.querySelector('text');
const r = new ResizeObserver(textResize(t));
r.observe(t);
const textResize = t => ([ a ]) => {
const e = t.parentNode.lastElementChild; // <svg> | <canvas>
const i = PDFPageElement.image(e); // { height, width };
const h = e.clientHeight;
const x = h / i. height;
const w = e.clientWidth;
const y = w / i. width;
t.style.setProperty('transform', `scale(${x}, ${y})`);
};
PDFPageElement.image = i => { if (!i) return;
switch (i.tagName) {
case 'CANVAS': return { height: i.height, width: i.width };
default: /*SVG*/ return { height: i.height.baseVal.value, width: i.width.baseVal.value };
}
};
with 1 additional CSS rule
.textLayer { overflow: visible; }
Before / After
Given viewport width and height, a one-time conversion from <span style> pixels to percents:
const px2pc = ({ width, height }) => s => {
const c = s.style;
const l = +c.getPropertyValue('left' ).slice(0, -2); // drop px
const t = +c.getPropertyValue('top' ).slice(0, -2);
const f = +c.getPropertyValue('font-size').slice(0, -2);
c.setProperty ('left', `${(l / width) * 100}%`);
c.setProperty ('top', `${(t / height) * 100}%`);
c.setProperty ('font-size', `${(f / height) * 100}%`);
};
and accounting for font size adaptation in <text> element whenever its ancestors cause resize:
const t = document.querySelector('text');
const r = new ResizeObserver(textFontResize(t));
r.observe(t);
const textFontResize = t => ([ a ]) => {
const i = t.parentNode.lastElementChild; // <svg> | <canvas>
t.style.setProperty('font-size', `${i.clientHeight}px`);
};
proved itself a very robust and relatively simple solution.
(If anyone comes up with a more elegant way, say without ever resorting to ResizeObserver, please post a new answer.)
Demo
(External assets are version-fixed for this question.)
Scroll to the end of this answer
Hit ▶️ Run code snippet
Hit ⤤ Full page
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.min.js" integrity="sha512-Z8CqofpIcnJN80feS2uccz+pXWgZzeKxDsDNMD/dJ6997/LSRY+W4NmEt9acwR+Gt9OHN0kkI1CTianCwoqcjQ==" crossorigin="anonymous"></script>
<script src="//shin.glitch.me/shin.q1.js"></script>
<script src="//shin.glitch.me/pdf.q1.js"></script>
<!-- private resources -->
<link href="//cdn.blue/{fa-5.15}/css/all.css" rel="stylesheet">
<link href="//cdn.blue/{fa+}/var.css" rel="stylesheet">
<link href="//cdn.blue/{fa+}/x.css" rel="stylesheet">
<!-- private resources -->
<style>:root { max-width: 50em; margin: auto; }</style>
<script>console.clear();</script>
<style>html, body { padding: 0; margin: 0; font-family: system-ui; }</style>
<script>
class CodeEditElement extends ShinElement {
constructor() {
super(`
<style>:host { display: block; overflow: hidden; } pre { height: 100%; margin: 0; }</style>
<pre contenteditable spellcheck=false inputmode=text></pre>`, { _: { QS: { T: [ 'pre' ] } } });
const e = this;
e.ph = v => { const e = v.target; if (!e.childElementCount) return; e.textContent = e.textContent; };
}
connectedCallback() { this._.pre. addEventListener('input', this.ph); }
disconnectedCallback() { this._.pre.removeEventListener('input', this.ph); }
get textContent() { return this._.pre.textContent; }
set textContent(v) { this._.pre.textContent = v; }
}
CodeEditElement.define();
class CodeLiveElement extends ShinElement {
constructor() { super(`<live></live>`, { _: { QS: { T: [ 'live' ] } } }); }
get textContent() { return this._.live.textContent; }
set textContent(v) { this._.live.textContent = v; }
get innerHTML() { return this._.live.innerHTML; }
set innerHTML(v) { this._.live.innerHTML = v; this.evalScripts(); }
evalScripts() { this._.QA('script').forEach(s => eval(s.textContent)); }
}
CodeLiveElement.define();
class CodePlayElement extends ShinElement {
constructor() {
super(`
<style>
:host(:not([open])) > code-edit { display: none; }
:host > div { display: flex; justify-content: stretch; align-items: stretch; }
::slotted(select) { flex: 1; }
* { border-color: var(--bd); }
</style>
<div part=controls>
<slot></slot>
<button id=reset><slot name=reset></slot></button>
<button id=open><slot name=open></slot></button>
</div>
<code-edit id=pre part=edit></code-edit>`,
{ _: { QS: { S: { '#pre': 'pre', '#reset': 'reset', '#open': 'open' } } } }
);
const e = this;
e.sc = v => { const tx = e.tx; e.px = tx; };
e.pi = v => { e.t.ux = e.px; };
e.rc = v => { e.tr(); };
e.oc = v => { e.open =!e.open; };
Shin.IPA(e, 'open', { t: Shin.Boolean });
}
connectedCallback() { setTimeout(() => this._init()); }
disconnectedCallback() { this._dest(); }
static cleanCode(t = "") {
return t.trim().replace(/^[\n]+/g, '').replace(/\s+$/g, '').split('\n').map(l => l.trimEnd()).join('\n');
}
get s() { return this.QS('select'); }
get S() { const o = this.QA('option'); return o.filter(o => o.selected); }
get t() { return [].concat(...this.S.map(o => Shin.QA('template', o))); }
get tx() { return this.t.map(t => t.ux || this.constructor.cleanCode(t.innerHTML)).join('\n'); }
tr() { this.t.ux = undefined; this.sc(); }
get r() { return this._.reset; }
get o() { return this._.open; }
get p() { return this._.pre; }
get P() { return this._.QA('pre'); }
get px() { return this.p.textContent; }
set px(v) { this.p.textContent = v; this.oninput(); }
_init() {
const e = this; e.sc();
e.s.addEventListener('change', e.sc);
e.p.addEventListener('input', e.pi);
e.r.addEventListener('click', e.rc);
e.o.addEventListener('click', e.oc);
}
_dest() {
const e = this;
e.s.removeEventListener('change', e.sc);
e.p.removeEventListener('input', e.pi);
e.p.removeEventListener('click', e.rc);
e.p.removeEventListener('click', e.oc);
}
}
CodePlayElement.define();
</script>
<style>
body { padding: 1em; overflow: scroll; font-family: system-ui; }
:root {
--list-bg: #eee;
--code-bg: #fefefe;
--live-bg_: #ccc;
--bd: #ccc;
}
code-play { display: flex; width: 100%; flex-direction: row-reverse; }
code-play:not(:first-of-type) { margin-top: 1em; }
::part(edit) { min-height: 1em; min-width: 1em; overflow-x: auto; background-color: var(--code-bg); }
x[undo]:before, x[undo]:after { content: var(--fa-undo); }
x[open]:before, x[open]:after { content: var(--fa-eye-slash); }
[open] x[open]:before,
[open] x[open]:after { content: var(--fa-eye); }
select { background: var(--list-bg); border-color: var(--bd); overflow: auto; }
live { background: var(--live-bg); display: block; bordxer: 1px solid var(--bd); }
code-play:not([open]) + live { _display: none; }
::part(edit) { border: 1px solid var(--bd); flex: 1; }
::part(controls) { flex-direction: column-reverse; }
::part() { border-radius: 3px; }
</style>
<style>
code-play:not([open]) { height: 2.7em; _outline: 1px solid; }
code-play:not([open]) > select { display: none; }
</style>
</head>
<body>
<code-live id="cl"></code-live><script>cl.innerHTML = "";</script>
<script>
const pes = 'previousElementSibling';
const n = (p, N = 1) => e => { let j = e[p]; for (let i = 1; i < N; i++) j = j[p]; return j; };
const c = n(pes, 2);
const l = n(pes, 1);
const _ = () => document.currentScript;
</script>
<code-play open>
<select multiple size="1">
<option selected>file<template>
<pdf-file id="f" src="//pdf.systems/16003.pdf"></pdf-file>
<pdf-file id="g" src="//pdf.systems/16004.pdf"></pdf-file>
</template></option>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{
const _ = document.currentScript;
c(_).oninput = () =>
l(_).innerHTML = c(_).px;
}
</script>
<code-play open style="min-height: 11em;">
<select multiple size="6">
<optgroup label="File Reference">
<option>by attribute<!-- !!!!!!!!! --><template>
<pdf-page of="f" no="+1" scale=".1"></pdf-page>
<pdf-page of="f" no="+1" scale=".2"></pdf-page>
<pdf-page of="f" no="+1" scale=".3"></pdf-page>
<pdf-page of="f" no="+1" scale=".4"></pdf-page>
<pdf-page of="f" no="+1" scale=".5"></pdf-page>
<pdf-page of="f" no="+1" scale=".5" svg=""></pdf-page>
</template></option>
<option>by ancestry<!-- !!!!!!!!! --><template>
<pdf-file src="//pdf.systems/16008.pdf">
<pdf-page no="+1" scale=".4" svg></pdf-page>
<pdf-page no="+3" scale=".4" svg></pdf-page>
<pdf-page no="-1" scale=".4" svg></pdf-page>
</pdf-file>
</template></option>
</optgroup>
<optgroup label="Embed Mode">
<option selected>Sized Container ⭤<!-- !!!!!!!!! --><template>
<style>
view { width: 10em; height: 25em; /* ← */
display: block; background: white; overflow: auto;
}
pdf-page { width: 100%; }
::part(layer) { width: 100%; height: auto; }
</style>
<view onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" xvg="" scale=".2"></pdf-page>
<pdf-page of="f" no="+1" xvg="" scale="1"></pdf-page>
<pdf-page of="f" no="+1" xvg="" scale="2"></pdf-page>
<pdf-page of="f" no="+1" svg=""></pdf-page>
<pdf-page of="f" no="-1" xvg=""></pdf-page>
<pdf-page of="g" no="-1" svg=""></pdf-page>
</view>
</template></option>
</optgroup>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{ const _ = document.currentScript; c(_).oninput = () => l(_).innerHTML = c(_).px; }</script>
<style>live { display: flex; align-items: flex-end; flex-wrap: wrap; } pdf-file { display: contents; }</style>
<h3>Styling</h3>
<p>Styles can be easily applied. (Try <strong><kbd>Ctrl</kbd></strong> + <i class="fa fa-mouse-pointer"></i> to unselect / select multiple.)</p>
<code-play open>
<select multiple size="8">
<optgroup label="Page">
<option>outline <template><style>pdf-page { outline: 1px dotted; }</style></template></option>
<option>background<template><style>pdf-page { background-color: rgb(200, 200, 255, .1); }</style></template></option>
</optgroup>
<optgroup label="Text">
<option selected>mark<template><style>::part(span) { background-color: rgb(255, 0, 0, .1); }</style></template></option>
</optgroup>
<optgroup label="Image">
<option>hidden <template><style>::part(image) { opacity: 0; }</style></template></option>
<option>pixelated <template><style>::part(image) { image-rendering: pixelated; }</style></template></option>
<option>crisp-edges<template><style>::part(image) { image-rendering: crisp-edges; }</style></template></option>
</optgroup>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{ const _ = document.currentScript; c(_).oninput = () => l(_).innerHTML = c(_).px; }</script>
<script>
document.addEventListener( 'load', e => console.warn('l', e.target));
document.addEventListener('unload', e => console.warn('u', e.target));
</script>
<p style="margin-bottom: 10em;">Documentation (WIP)</p>
</body>
</html>
It's not really possible to do via CSS in a clean way. This, for example, will work, but because you're positioning a span on top of a picture, all of the numbers are hardcoded:
.textLayer > span{
right: 10% !important;
left: auto !important;
top: 0 !important;
margin-top: 6%;/*margin-top uses 6% of the WIDTH, not 6% of the height. It's very useful when trying to place something on top of an image.*/
width: 20%;
height: 2%;
}
Here's a reproduction of your snippet with the CSS added in:
#namespace url(http://www.w3.org/1999/xhtml);
#namespace svg url(http://www.w3.org/2000/svg);
/*pdf.css*/
:root {
--pdf-page-outline-color: #aaa;
--pdf-page-background-color: #fcfcfc;
}
pdf-file { display: contents; }
pdf-page {
display: inline-block;
outline: 1px solid var(--pdf-page-outline-color);
background-color: var(--pdf-page-background-color);
}
pdf-page { position: relative; }
/* text.css */
.textLayer {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
width: 100%; height: 100%;
-overflow: hidden;
opacity: 1;
line-height: 1;
}
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.textLayer > span{
right: 10% !important;
left: auto !important;
top: 0 !important;
margin-top: 6%;/*margin-top uses 6% of the WIDTH, not the height. It's sometimes more useful than ordinary top:6%.*/
width: 20%;
height: 2%;
}
/**/
view { background: green; }
.textLayer { background: rgba(0, 255, 0, .1); }
svg|svg { background: rgba(255, 0, 0, .1); }
<style>
view {
height: 45em; /* ← */
display: flex;
overflow: auto;
flex-direction: column;
place-items: center;
scroll-snap-type: y mandatory;
overflow: auto;
}
pdf-page { height: 100%; scroll-snap-align: start; }
svg { height: 100%; width: auto; }
text { overflow: visible; background: rgb(0, 0, 0, .1); }
text > span { background: rgba(0,0,255,.1); }
</style>
<view -onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" svg="">
<text class="textLayer">
<span style="left: 417.34px; top: 37.8391px; font-size: 12px; font-family: sans-serif; transform: scaleX(1.07482);">Plenarprotokoll 16/3</span>
</text>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842">
<g transform="matrix(1 0 0 -1 -8 850)">
<g transform="">
<text transform="matrix(12 0 0 12 425.34 801.2976) scale(1, -1)" xml:space="preserve">
<tspan x="0 0.6672 0.9454 1.5016 2.1128 2.669 3.0582 3.6694 4.0586 4.6698 5.003 5.6142 6.1704 6.7816 7.0598 7.6132 8.1694 8.7256 9.0038" y="0" font-family="g_d0_f1" font-size="1px" fill="rgb(0,0,0)"></tspan>
</text>
</g>
</g>
</svg>
</pdf-page>
</view>
Edit: A bit more clarification.
We want to put the box on top of the text. The numbers used for the position and width/height of the text may seem arbitrary, but that's simply because the location of the item we're trying to cover ALSO has an arbitrary location/width/height. (If you like, we can talk about how to use GIMP to check the aspect ratio of your image, but..
a. I don't think using GIMP to measure the correct values is within the scope of this answer (You can math it out by taking the width of the image and height of the image to find the aspect ratio, and then use that aspect ratio along with the X/Y coordinates of the start point and the X/Y coordinates of the end point to figure out what percentages you need to use.... but, well....)
b. It's usually significantly faster to just fiddle with it in Chrome's Dev Tools for 15 minutes,
As a general rule, when using position: absolute to put something on top of an image, your code is going to look something like this:
.item{
position:absolute;
top:0;
margin-top:W%; //The reason we use margin instead of top is because margin is based off width, which allows us to maintain aspect ratio on our positioning.
left:X%; // Or right
width:Y%;
height:Z%;
}
Edit 2: I'd originally used vw and vh, which are often extremely useful for this kind of positioning, but in the end it was possible to refactor them out, which is why the only non-standard positioning we're using is margin-top.
So I am not using any CSS framework like bootstrap to get responsiveness out of the box which is why I am having trouble making responsive layout.
Please see jsbin
I essentially what to auto-resize colorful boxes based on browser window size eg that should shrink or grow automatically based on window size. Colorful boxes inside their parent should always be in horizontal row but should be able to adjust their width and height like this example.
I tried using flex-wrap: nowrap; but it didn't do the trick :(
Please note that colorful boxes are using position:absolute with parent's position being relative. I am also adding left css property to these boxes via JavaScript to change their position for the sake of sliding animation.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
let width = options.width || 200;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
width: auto;
height: auto;
margin: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.carousel {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.carousel .carousel-item {
position: absolute;
transition: transform .5s ease-in-out;
color: #fff;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
padding-top: 150px;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
margin-top: 150px;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
top: 104px;
box-shadow: 0 1px 0 #999;
}
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
Problem here was:
Width was hard-coded in your JS, so if width is in px it can't be responsive.
By applying position:absolute to you carousel-item, it forced the children to get out of the box.
What I did:
Got rid of the static width and other functionalities related to width from your JS
Removed position:absolute from carousel-item
Let me know if this is what you are expecting.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
// let width = options.width || 100;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
// slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
// slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
// elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
// slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
height: auto;
margin: 25px;
display: flex;
}
.carousel {
flex: 1;
height: 100%;
width: 100vh;
/* overflow:hidden; */
display: flex;
align-items: center;
justify-content: center;
}
.carousel .carousel-item {
transition: transform .5s ease-in-out;
color: #fff;
flex: 1;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
box-shadow: 0 1px 0 #999;
}
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>
I've encountered a very strange bug in Google Chrome!
if I use mix-blend-mode property on en element then if I apply transform: translateZ(50px); to an element below it, it doesn't work!
Please see this:
function threeD_hover(params) {
// Get the elements inside to be animated ...
var el = params.parent.find(params.element),
// defining the factor to be whether concave or covex style ...
factor = 1;
if (params.convex) {
factor = -1;
}
// Set the nessecory styles
TweenMax.set(el, {
transformPerspective: params.perspective,
rotationY: 0.01,
rotationX: 0.01,
"transform-style": "preserve-3d",
"backface-visibility": " hidden"
});
// Thee core function fo each of the elements inside ...
el.each(function(index, el) {
var $this = $(this);
function core_func(e) {
// Defining the degrees ..
var ax = params.xSensitivity * (($this.offset().left + ($this.outerWidth() / 2)) - e.pageX) / 200,
ay = params.ySensitivity * (($this.offset().top + ($this.outerHeight() / 2)) - e.pageY) / 200;
// Setting the max deg amount ...
if (ax > params.maxRotateDegreeX) {
ax = params.maxRotateDegreeX;
} else if (ax < -params.maxRotateDegreeX) {
ax = -params.maxRotateDegreeX;
}
if (ay > params.maxRotateDegreeY) {
ay = params.maxRotateDegreeY;
} else if (ay < -params.maxRotateDegreeY) {
ay = -params.maxRotateDegreeY;
}
var dx = (-factor) * ax,
dy = factor * ay;
// Animating ...
TweenMax.to($this, params.movementTime, {
rotationY: dx,
rotationX: dy,
ease: params.easing
});
}
if (!params.mouseSensitiveArea) {
params.mouseSensitiveArea = params.parent;
}
// mosuse move on canvas ..
params.mouseSensitiveArea.on("mousemove", core_func);
});
}
threeD_hover({
parent: $(".section"),
element: $(".card"),
// convex: true, // if not set it is false or concave
maxRotateDegreeX: 30,
maxRotateDegreeY: 30,
xSensitivity: 5, // Min: 1 | Max: 10
ySensitivity: 10, // Min: 1 | Max: 10
perspective: 1000,
// mouseSensitiveArea: $window, // if not set it's the parent element
easing: Power4.easeOut,
movementTime: 1
});
.parent {
background-image: url("http://www.intrawallpaper.com/static/images/background-wallpapers-26_HgdHzBm.jpg");
font-family: "Playfair Display", georgia, serif;
}
.mix-blend-overlay {
mix-blend-mode: overlay;
color: #fff;
text-align: center;
}
.section {
padding: 20px 0;
display: block;
}
.card {
pointer-events: none;
padding: 20px;
background: white;
border-radius: 5px;
width: 400px;
height: 150px;
margin: auto;
transform-style: preserve-3d;
backface-visibility: hidden;
display: flex;
box-shadow: 0 0 5px rgba(0, 0, 0, .1);
position: relative;
}
.card-content {
margin: auto;
text-align: center;
transform-style: preserve-3d;
}
h1 {
transform: translateZ(100px);
}
p {
transform: translateZ(50px);
display: block;
}
p.related {
transform: translateZ(80px);
font-style: italic;
}
a {
color: #69c6b8;
pointer-events: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
<div class="section">
<div class="card">
<div class="card-content">
<h1>Just hover around</h1>
<p><small>by Ershad</small></p>
<p class="related"><strong>This has been done using TweenMax </strong></p>
</div>
</div>
</div>
<h1 class="mix-blend-overlay">Ershad</h1>
<div class="section">
<div class="card">
<div class="card-content">
<h1>Just hover around</h1>
<p><small>by Ershad</small></p>
<p class="related"><strong>This has been done using TweenMax </strong></p>
</div>
</div>
</div>
</div>
The 2 Cards on that demo have exactly the same structure, style and js function yet the latter won't work properly because and element with mix-blend-property is before it, try to remove it and it works just fine!!
How can I solve this problem?!