Recreating Medium's Image Fade Scroll Effect - javascript

Unfortunately, I'm not much of a jQuery guru, I'm trying to recreate the image fade/blur featured on medium. Here's an example -https://medium.com/matter/76d9913c6011
If you scroll down the page you'll notice some images, that progressively fade in/out as you scroll. Upon inspecting the code, I noticed there are two version of the image to create the effect, a regular version and another which has been blurred.
As far as I know, things are setup like this:
<section class=
"section-bottom-center section-image-full section-using-canvas"
data-scroll="native">
<div class="section-background" data-height="2130" data-image-id=
"1*ladXngaMeWEqp1R18uSSQA.jpeg" data-scroll="post-section" data-width=
"3200">
<div class="section-background-image" style=
"background-image: url(https://d262ilb51hltx0.cloudfront.net/max/700/gradv/29/81/40/1*ladXngaMeWEqp1R18uSSQA.jpeg);">
</div><br>
</div>
<div class="section-inner layout-single-column">
<p>SOME TEXT THAT FADES OVER THE IMAGE</p>
</div>
</section>
I found some of the jQuery, unfortunately I don't really understand it that well, so I can't recreate it.
var qm = function(a, b) {
var c = '\x3cdiv class\x3d"section-background' + (a.hasFocus ? " media-has-focus" : "") + '"' + ("section-image-full" == a.imageLayout ? ' data-scroll\x3d"post-section"' : "") + 'data-image-id\x3d"' + (0,z.N)(a.backgroundImage.id) + '"', d;
d = a.backgroundImage;
d = (d.originalWidth ? ' data-width\x3d"' + (0,z.N)(d.originalWidth) + '"' : "") + (d.originalHeight ? ' data-height\x3d"' + (0,z.N)(d.originalHeight) + '"' : "") + (d.filter ? ' data-filter\x3d"' + (0,z.N)(d.filter) + '"' : "") + (d.backgroundSize ? ' data-image-style\x3d"' + (0,z.N)(d.backgroundSize) + '"' : "");
c = c + d + "\x3e";
c = "section-image-full" == a.imageLayout ? c + ('\x3cdiv class\x3d"section-background-image" style\x3d"background-image: url(' + (0,z.N)((0,z.P)(b.miroUrl)) + "/max/" + (0,z.N)("700".replace(ji, ki)) + "/gradv/29/81/40/" + (0,z.N)(String(a.backgroundImage.id).replace(ji, ki)) + ');"\x3e\x3c/div\x3e\x3cbr\x3e') : "section-image-left" == a.imageLayout || "section-image-right" == a.imageLayout ? c + ('\x3cimg src\x3d"' + (0,z.N)((0,z.P)(b.miroUrl)) + "/max/" + (0,z.N)(b.postColumnWidth) + "/multiply/grey/" +
(0,z.N)(a.backgroundImage.id) + '"\x3e') : c + "\x3cbr\x3e";
return c + "\x3c/div\x3e";
};
One thing I did notice, is rather than the the two images simply fading in/out - the fading level is somehow bound to the scroll, so when you stop scrolling the images stop fading. This is a really nice effect.
P.S I'm looking to do this as simply as possible, preferably without jQuery plugins.
Thanks

Check out Andreas Storm's Medium Blur scrolling effect on Codepen.
HTML:
<div class='blurImg'>
<div style="background-image: url('https://d262ilb51hltx0.cloudfront.net/fit/c/1600/1280/gradv/29/81/60/darken/25/1*4ncz3hLxmL8E_bUh-0z62w.jpeg')"></div>
<div class='blur' style="background-image: url('https://d262ilb51hltx0.cloudfront.net/fit/c/1600/1280/gradv/29/81/40/darken/50/blur/50/1*4ncz3hLxmL8E_bUh-0z62w.jpeg')"></div>
</div>
<header>
<div>
<h1>
Medium
</h1>
<p>
Everyone’s stories and ideas
</p>
<a href='https://medium.com/' title='Medium'>Learn more</a>
</div>
<nav role='navigation'>
<ul>
<li>
<a class='active' href='#'>Reading List</a>
</li>
<li>
<a href='#'>Bookmarks</a>
</li>
<li>
<a href='#'>Top 100</a>
</li>
</ul>
</nav>
</header>
<div class='container'>
<div></div>
</div>
CSS:
body
font-size 14px
font-family Sans-Serif
*
box-sizing border-box
a
text-decoration none
.blurImg
position relative
width 100%
height 440px
z-index -1
top 0
left 0
& > div
position fixed
width 100%
height 440px
background-repeat no-repeat
background-size cover
background-position center center
.blur
opacity 0
header
z-index 1
position absolute
top 0
width 100%
padding 0px 20px
& > div
max-width 600px
margin 0 auto
padding-top 150px
height 380px
text-align center
color White
a
font-size 0.8em
letter-spacing 0.08em
color rgba(255,255,255,0.85)
line-height 30px
padding 7px 14px
border 1px solid rgba(255,255,255,0.3)
border-radius 2em
transition all 0.3s ease
&:hover
background white
color Gray
p
font-size 1.5em
margin-bottom 0.7em
font-family Times New Roman
h1
font-weight 800
font-size 3.4em
margin-bottom 0.2em
nav
max-width 600px
margin 0 auto
height 60px
border-top 1px rgba(255,255,255,0.35) solid
ul
li
display inline-block
margin-right 20px
a
font-weight 800
font-size 11px
text-transform uppercase
letter-spacing .2em
color rgba(255,255,255,0.5)
transition color 0.3s linear
line-height 60px
display block
&.active
box-shadow 0px -1px 0px white
nav ul li a.active, nav ul li a:hover
color white
.container
height 1300px
background white
z-index 1
padding 0px 20px
div
max-width 600px
margin 0 auto
padding-top 40px
CoffeeScript:
$(window).scroll ->
oVal = ($(window).scrollTop() / 240)
$(".blur").css "opacity", oVal

I also was trying to figure out how they managed this at Medium.
The minified javascript snippet you are pointing out doesn't seems to me being responsible for the blur effect. In my opinion they also process through a canvas.
Check out this SO answer:
Coderwall blurred background effect with canvas
hope that helps. (unfortunately this uses a bit of jquery but you can skip that part ;) )

Related

How to get the current Viewable elements in a page using javascript?

I'm making a nextjs app ,
I want to change the navbar active button according the the visible sections in the viewport
the page children look like this
<div id="section1" > </div>
<div id="section2" > </div>
...
<div id="faq" > </div>
<div id="contact" > </div>
for example when the user scrolls and the current div in the screen is the contact form
I set it the route to /#contact
My main problem is how to detect the current viewable element in the viewport
How can I achieve this ?
You can use Intersection Observer. Here is a live demo, where the list items on the header are automatically highlighted as you scroll over each section.
Javascript:
// simple function to use for callback in the intersection observer
const changeNav = (entries, observer) => {
entries.forEach((entry) => {
// verify the element is intersecting
if(entry.isIntersecting && entry.intersectionRatio >= 0.55) {
// remove old active class
document.querySelector('.active').classList.remove('active');
// get id of the intersecting section
var id = entry.target.getAttribute('id');
// find matching link & add appropriate class
var newLink = document.querySelector(`[href="#${id}"]`).classList.add('active');
}
});
}
// init the observer
const options = {
threshold: 0.55
}
const observer = new IntersectionObserver(changeNav, options);
// target the elements to be observed
const sections = document.querySelectorAll('section');
sections.forEach((section) => {
observer.observe(section);
});
HTML:
nav
ul
li
a(href='#one').active One
li
a(href='#two') Two
li
a(href='#three') Three
li
a(href='#four') Four
li
a(href='#five') Five
section#one
p Slide One
section#two
p Slide Two
section#three
p Slide Three
section#four
p Slide Four
section#five
p Slide Five
CSS:
nav
position fixed
width 100%
top 0
left 0
right 0
background-color white
ul
list-style-type none
display flex
align-items center
justify-content space-around
width 100%
max-width 800px
height 50px
margin 0 auto
padding 0
li
display inline-block
padding 5px
a
display block
height 40px
padding 0 20px
line-height 40px
text-decoration none
text-transform uppercase
color #323232
font-weight bold
border-radius 4px
transition background-color 0.3s ease-in
&:hover
&:active
&:focus
background-color rgba(#B8D6A8, 0.5)
&.active
background-color rgba(#B8D6A8, 0.5)
section
display flex
align-items center
justify-content center
min-height 100vh
p
text-align center
color white
font-size 3.5em
font-weight bold
text-transform uppercase
#one
background-color #6CA392
#two
background-color #FFA58C
#three
background-color #FF4F30
#four
background-color #576B51
#five
background-color #392A1B

Setting `element.style.height` not working as expected if `element.style.box-sizing` is `border-box`

I am having an understanding problem with the following code:
let myDiv1 = document.getElementById("myDiv1");
alert("Click me to make 'Hello' vanish");
myDiv1.style.height = "0px";
let myDiv2 = document.getElementById("myDiv2");
alert("Click me to make 'World' vanish");
myDiv2.style.height = "0";
.myClass1 {
box-sizing: border-box;
overflow: hidden;
padding-top: 2em;
padding-bottom: 2em;
background-color: yellow;
}
.myClass2 {
box-sizing: content-box;
overflow: hidden;
padding-top: 2em;
padding-bottom: 2em;
background-color: orange;
}
<body>
<div id="myDiv1" class="myClass1">
Hello
</div>
<div id="myDiv2" class="myClass2">
World
</div>
</body>
I understand the behavior of the second (orange) div: It has box-sizing: content-box;, so its height does not include the padding or the borders. Hence, when its height is set to 0, it shrinks basically by the height of the text "World", but the padding is left as-is. Since the padding exceeds the original text height, the text is still visible in the padding. Only that part of padding which is now outside the div (due to the reduced height) becomes invisible (due to overflow: hidden;).
I do not understand the behavior of the first (yellow) div, though. It has box-sizing: border-box;, so its height does include the padding and the borders. Hence, when its height is set to 0, it should shrink to "real" zero height, meaning that the text, the padding and the borders then should be outside the div and thus should be invisible (due to overflow: hidden;).
Can anybody explain why this is not the case and why the first div behaves just like the second div?
P.S. Tested in Firefox and Chrome, both up-to-date (production channel) at the time of writing this.
border-box tells the browser to account for any border and padding in the values you specify for an element's width and height. If you set an element's width to 100 pixels, that 100 pixels will include any border or padding you added, and the content box will shrink to absorb that extra width. This typically makes it much easier to size elements. ref
Here is an example to better ilustrate your issue:
.box {
display: inline-block;
padding-top: 2em;
padding-bottom: 2em;
border: 2px solid blue;
background: linear-gradient(red, red) content-box, orange;
height: 100px;
animation:move 5s linear infinite alternate;
}
#keyframes move{
to {
height:0;
}
}
<div class="box">
World
</div>
<div class="box" style=" box-sizing: border-box;">
World
</div>
The first example is the trivial one where we decrease the height (red area) until 0 and the animation never stop.
In the second case the height include the padding and border so before reaching 0 the content area is already 0 that's why the animation seems to stop because we cannot decrease more than 0 and the border/padding already consumed all the space.
It's logical that when height is equal to 0 both are the same since in the first one we tell the browser that the content area should be 0 (we don't care about padding/border) and in the second case we told the browser to account for the padding/border into the height we specified so we have less room for the content area and since we cannot have less than 0 then its 0.
In other words, you aren't setting the total height that will be split between the content area, padding and border but you are setting a height and the browser is shrinking it as much as possible to include the border and padding.
Related for more examples and details: box-sizing: border-box with no declared height/width

Self adjustable CSS border width [duplicate]

This question already has answers here:
How to set border's thickness in percentages?
(9 answers)
Closed 8 years ago.
I have a div element as below for example:
<div style="height: 10%; width: 20%; border: 1px solid black">
Div Content
</div>
Since the above div has height and width in %, I want the border width also to adjust when we resize the element. Looks like there is no way of denominating border width apart from pixel. So is there any idea on how to adjust the border width?
You can use jQuery. for example: var x = $(".div-class").width() or .height now you have div height or width in pixels. make some calculations to get border height. for exaple we need 10% of height. var borderHeight = x *0.1; now just give it for div $("div-name").css("border-size",borderHeight + "px");
You can do it with percentage padding and a wrapper with background color :
<div class="wrapper">
<div class="content">
Div Content
</div>
</div>
.wrapper {
background: #000;
padding: 1%;
width: 20%;
height: 10%;
}
.content {
background: #fff;
}
Link to jsfiddle : http://jsfiddle.net/rckbk9p6/

JQuery : BoxShadow and Color Change using "OnChange"

I'm new to Javascript and JQuery so please be patient. I want to make a box-shadow that changes color when using a color picker. For this I'm using Jscolor.js. It's my understanding that I need to use a Onchange function and use +this.color to select the current color.
However I can't seem to get the code to work, I've tried reading everywhere but there isn't much information on the subject I need. I have created a JsFiddle as a example.
Thank you very much for the help in advance.
CODE:
JAVASCRIPT:
$(document).ready(function(){
$('#content').css('boxShadow', '+this.color inset 0 200px 400px -200px');
$('#content').css('boxShadowColor', '+this.color');
});
HTML:
<div id="content">
<p>
<input class="color" onchange="$('#content').css('boxShadow','+this.color');" />
<h1> This is a example.</h1>
</div>
</div>
CSS:
.content {
-webkit-box-shadow: inset 0 200px 400px -200px #000);
-moz-box-shadow: inset 0 200px 400px -200px #000);
box-shadow: inset 0 200px 400px -200px #000;
}
FIDDLE
HTML
<div id="content">
<p>
<input class="color" onchange="addNewBoxShadowStylesToHead('#frontcontent', '#'+this.color);" />
<h1> This is a example.</h1>
</p>
</div>
<div id="frontcontent">This div will have the effect</div>
JS
var head = document.head || document.getElementsByTagName('head')[0];
function addNewBoxShadowStylesToHead(selector,color) {
var css = selector + '{-webkit-box-shadow: inset 0 200px 400px -200px ' +color+ ';' +
'-moz-box-shadow: inset 0 200px 400px -200px ' + color + ';'+
'box-shadow: inset 0 200px 400px -200px ' + color + '; }',
style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet){
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
}
OR Simpler JS
function applyboxshadow(selector, color) {
$(selector).css({
'-webkit-box-shadow' : 'inset 0 200px 400px -200px ' + color,
'-moz-box-shadow': 'inset 0 200px 400px -200px '+ color,
'box-shadow': 'inset 0 200px 400px -200px '+ color
});
}
call it like so
<input class="color" onchange="applyboxshadow('#frontcontent', '#'+this.color);" />

How to fill page horizontally with divs without empty space at the end

I am having this simple but frustrating CSS problem. I am trying to fill a page completely with divs/boxes. The problem is, that these boxes have same width all the time, therefore boxes won't fill up evenly. Let me demonstrate this:
Fiddle.
CSS:
.box {
float: left;
width: 200px;
height: 200px;
margin: 0px 10px 10px 0px;
background-color: #000000;
}
HTML:
<div id="boxes">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
This is OK:
But if user has a smaller window size, it will go like:
This would be the best solution, resizing boxes to fit screen horizontally evenly:
What would be the best solution to make them fit whole page when window is resized? Preferably CSS-only (if possible).
Here is a working example.
IMO, the solution depends om your needs. If are able to scale the images, then the 33% rule offered by others here is probably acceptable. However, you may wish to limit how far the size can be go (min-width). Also, this is going to totally bork your aspect ratios, which you might find unacceptable.
One alternate solution would be to have a 'viewport' div on on-top of a larger div that allows some bleed-over to minimize the 'gosh, it's only one pixel only over' effect (where you get a huge, gaping blank column. This solution allows the cards to bleed out of the viewport a bit before forcing a new column. And it is CSS-only. This is the provided example. Test it out by changing the outer container width and height ('my-outer'):
/* CSS */
.my-outer {
position : relative;
box-sizing : border-box;
width : 350px;
height : 450px;
border : 1px solid red;
overflow-x : hidden;
overflow-y : auto;
}
.my-inner {
position : relative;
box-sizing : border-box;
border : 1px solid green;
width : 120%;
}
.my-card {
box-sizing : border-box;
float : left;
margin : 10px;
width : 100px;
height : 100px;
font-size : 50px;
line-height : 100px;
text-align : center;
font-weight : 800;
background : #aaf;
color : #fff;
border-radius : 2x;
box-shadow: 0 0 16px -2px #888;
}
<!-- HTML -->
<div class="my-outer">
<div class="my-inner">
<div class="my-card">1</div>
<div class="my-card">2</div>
<div class="my-card">3</div>
<div class="my-card">4</div>
<div class="my-card">5</div>
<div class="my-card">6</div>
<div class="my-card">7</div>
<div class="my-card">8</div>
<div class="my-card">9</div>
<div class="my-card">10</div>
<div class="my-card">11</div>
<div class="my-card">12</div>
<div class="my-card">13</div>
<div class="my-card">14</div>
<div stlye="clear : both;"></div>
</div>
Another alternate solution, which would probably provide the best user experience, would be to resize the container in steps. This would require some JavaScript, but can be fairly easy to quite difficult to implement depending about the environment. And yes, I have done this sort of thing before ;)
Try giving the container div boxes a width of the max amount of boxes + the margin for each box. In your example that would be 200 * 3 + 30. This is given that you only want three boxes per row.
something like
html:
<div class="box></div>
<div class="box></div>
<div class="box></div>
<div class="box></div>
<div class="box></div>
<div class="box></div>
css:
.box{
height: 100px;
width: 33%;
}

Categories