cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <div id="container">  
  
  <div id="maskSide"></div>
  <div id="maskBottom"></div>

  <div id="viewport">
    <div id="background" class="resize"></div>
    <div id="hills" class="scrolling resize"></div>
    <div id="town" class="scrolling resize">
      <canvas id="didyouhearthat"></canvas>
    </div>
    <div id="utilities" class="scrolling resize"><a href="https://tinymediaempire.tumblr.com/" target="_blank">"i didn't hear you arrive, i didn't hear you go" &copy; Daniel Danger</a></div>
    <div id="thevisitorPanel" class="scrolling resize">
          <div id="thevisitor" class="giant burp"></div>
    </div>
  </div>
  <div id="scrollHint" class="animate resize">
    <div class="chevron animated neon3"></div>
    <div class="line animated neon2"></div>
    <div class="line animated neon1"></div>
    <div class="title1">i didn't hear you arrive,</div>
    <div class="title2">i didn't hear you go.</div>
  </div>

</div>

            
          
!
            
              @import "compass/css3";

/***** MIXINS *****/
@mixin bgSize() {
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
  background-size: cover;
}

$easeInOutSine: all 1s cubic-bezier(0.39, 0.575, 0.565, 1);

// border box reset on all objects
*, *:before, *:after {
  //reset
  padding: 0;
  margin: 0;
  @include box-sizing(border-box);
}

body {
  font-family: 'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace;
}

#container {
  // height: 1200vh; -- can't use due to iOS bug
  height: 2500px;
  background-color: silver;
  position: relative;
  overflow: hidden;
  top: 0;
  left: 0;
}

#maskSide {
  position: fixed;
  top: 0;
  bottom: 0;
  right: 0;
  z-index: 1000;
  
  background-color: black;
}

#maskBottom {
  position: fixed;
  bottom: 0;
  right: 0;
  left: 0;
  z-index: 1000;
  
  background-color: black;
}

#viewport {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;  
  
  @include perspective(10px);
  @include perspective-origin(10% 25%)
  //@include transform-style(flat);
}

#background {
  // moon and red sky
  position: absolute;
  background:url(https://s3-us-west-2.amazonaws.com/evildonald.codepen.io/iDidntHearYouArrive/background.jpg) no-repeat;
  @include bgSize();
}

.scrolling {
  position: absolute;  
  @include transition($easeInOutSine);  
  @include opacity(0);
  @include translate3d(0,0,0);
}

#hills {
  background: url(https://s3-us-west-2.amazonaws.com/evildonald.codepen.io/iDidntHearYouArrive/hills.gif) no-repeat;
  @include bgSize();
  z-index: 10;
}
#town {
  // houses
  background: url(https://s3-us-west-2.amazonaws.com/evildonald.codepen.io/iDidntHearYouArrive/town.gif) no-repeat;
  @include bgSize();
  z-index: 20;
  
  #didyouhearthat {
    position: absolute;

    // broken on some browsers
    //height: 100%;
    //width: 100%;
  }

}
#utilities {
  background: url(https://s3-us-west-2.amazonaws.com/evildonald.codepen.io/iDidntHearYouArrive/lightpost.png) no-repeat;
  @include bgSize();
  z-index: 30;
  
  a {
    // Daniel Danger link
    color: #777;
    background-color: black; 
    font-size: 1em;
    padding: 2px;
    
    position: absolute;
    bottom: 0;
    right: 0;      
  }
}

#thevisitorPanel {
  z-index: 15;
  #thevisitor {
  background: url(https://s3-us-west-2.amazonaws.com/evildonald.codepen.io/iDidntHearYouArrive/giant.png) no-repeat;
    height: 100%;
    @include bgSize();
  }
}
/**** scroll hint ****/
$scrollHint-color: #F45300;

#scrollHint {
  position: relative;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2000;
  
  .title1, .title2 {
    text-align: center;
    font-size: 1em;
    margin: 0 auto;
    position: relative;    
  }
}

.chevron {
  position: relative;
  margin: 0 auto 1em auto;
  width: 1em; 
  height: 4em;
  border-radius: .5em/.25em;
}
.chevron:before, .chevron:after {
  position: absolute;
  background: $scrollHint-color;
  width: inherit; height: inherit;
  border-radius: inherit;
  content: '';
}
.chevron:before {
  @include transform(rotate(60deg) translateY(1.6em));
}
.chevron:after {
  @include transform(rotate(-60deg) translateY(1.6em));
}

.line {
  position: relative;
  margin: 1em auto;
  width: 6em; 
  height: 1em;
  border-radius: .5em/.25em;
  background: $scrollHint-color;
}

/************ ANIMATION ***************/
.animated { 
  @include transition(opacity .2s);
  @include animation-duration(2s);
  @include animation-fill-mode(both);
  @include animation-timing-function(ease-in); 
  @include animation-iteration-count(infinite); 
} 

@include keyframes(neon1) {
  0%, 15% { opacity: 0; }
  20% { opacity: 1; } 
  80% { opacity: 1; } 
  100% { opacity: 0; }
}
.neon1 {
    @include animation-name(neon1);
}
@include keyframes(neon2) {
  0% { opacity: 0; }
  35% { opacity: 0; }
  40% { opacity: 1; } 
  80% { opacity: 1; } 
  100% { opacity: 0; }
}
.neon2 {
    @include animation-name(neon2);
}
@include keyframes(neon3) {
  0% { opacity: 0; }
  55% { opacity: 0; } 
  60% { opacity: 1; } 
  80% { opacity: 1; } 
  100% { opacity: 0; }
}
.neon3 {
    @include animation-name(neon3);
}

/************ GIANT ***************/
.giant { 
  @include transition(all .5s);
  @include animation-duration(15s);
  @include animation-fill-mode(both);
  @include animation-timing-function(ease-in-out); 
  @include animation-iteration-count(infinite); 
  @include animation-name(burpAndSway);
} 

@include keyframes(burpAndSway) {
  10%, 13% { @include translateX(2px); }
  11%, 14% { @include translateX(-2px); } 
  9%, 13% { @include translateY(1px); }
  12%, 14% { @include translateY(-1px); } 
  // sway
  0%, 100% { @include skewX(0deg); }
  40% { @include skewX(-1.5deg); }
  75% { @include skewX(3deg); }  
  90% { @include skewX(-1.5deg); }  
}

/**** title text ***/
.title1, .title2 {
  @include transition(width .5s);
  @include animation-duration(30s);
  @include animation-fill-mode(both);
  @include animation-iteration-count(infinite); 
  overflow: hidden;
  white-space: nowrap;
  box-shadow: inset -250px 0 50px 10px transparent;
}

.title1 {
    @include animation-name(titleFade1);
}
.title2 {
    @include animation-name(titleFade2);
}

@include keyframes(titleFade1) {
    0% {width:0%;}
    50% {width:100%;}
    99% {width:100%;}
    100% {width:0%;}
}
@include keyframes(titleFade2) {
    0%, 15% {width:0%;}
    65% {width:100%;}
    99% {width:100%;}
    100% {width:0%;}
}

            
          
!
            
              /* TODO
*/
/******* Asset Controls ***********/
function init() {
  
  window.data = {};
  buildAssets();

  window.addEventListener("orientationchange", fixAspectRatio);
  window.addEventListener("scroll", listenScroll);
  window.addEventListener("resize", fixAspectRatio);
}

function buildAssets() {
  
  data.container = document.getElementById("container");
  data.maskSide = document.getElementById("maskSide");
  data.maskBottom = document.getElementById("maskBottom");
  data.canvas = document.getElementById("didyouhearthat");
  data.resizeItems = document.getElementsByClassName("resize");
  data.scrollHint = document.getElementById("scrollHint");

  if (data.canvas && data.canvas.getContext) {
	  data.canvasContext = data.canvas.getContext("2d");
  }

  
  data.panels = {}
  data.panels.hills = document.getElementById("hills");
  data.panels.town = document.getElementById("town");
  data.panels.utilities = document.getElementById("utilities");
  data.panels.thevisitorPanel = document.getElementById("thevisitorPanel");

  data.lastScrollPos = -100; // always render first scroll
  
  // controls the axis and direction of scroll
  data.scale = {
    "hills":{"y":.5, "z":2}, 
    "town":{"y":.8, "z": 10}, 
    "utilities":{"y":2.75, "z":20}, 
    "thevisitorPanel":{"x":3.5}
  };

  // original 2D scroll
  //data.scale = { "hills":{"y":.65}, "town":{"y":1.25}, "utilities":{"y":2.5}, "thevisitorPanel":{"x":3.5}};

  // position of the window shutters
  data.houses = {
    "house1" : {"windows" : [
      [10.5,57.3,1,3.6],
      [13,57.3,1.2,3.8],
      [15.3,57.3,1.1,3.7],
      [21.25,58.4,1,2.2]
    ]},
    "house2" : {"windows" : [
      [8.1,49.1,1.5, 3.7],
      [11.25,48.85,1.6,3.5],
      [14.2,48.85,1.75,2.2],
      [18.75,50.20,1.3,3.8],
      [21.5,50.25,1,4]
    ]},
    "house3" : {"windows" : [
      [89.8,60.8,2.1,3.5],
      [83.9,59.8,2,6],
      [80.75,59.65,1.1,5.5]
    ]}
  };
    
  listenScroll(); // fire one time
  fixAspectRatio(); // fire one time
  canvas.didYouHearThat(); // canvas window sprites
  
}

// much faster than jQuery scrollTop
function scrollTop() {
  var value = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
  return value;
}

// when we scroll, see how far we've moved as a % and apply that to the image panels multipied by their scale
function listenScroll(evt) {
  var scrollPos = scrollTop();
  var endScrollPos = (data.container.offsetHeight - window.innerHeight) - scrollPos;
  var diff = data.lastScrollPos - scrollPos;
  var sensitivity = 125;
  if (diff > sensitivity || diff < (-1 * sensitivity) || scrollPos < sensitivity || endScrollPos < sensitivity) {
    data.lastScrollPos = scrollPos;
    var percent = 1 - scrollPos / (data.container.offsetHeight - window.innerHeight);
        
    setPanelOpacity("hills", setPanelPosition("hills", percent));
    setPanelOpacity("town", setPanelPosition("town", percent));
    setPanelOpacity("utilities", setPanelPosition("utilities", percent));
    setPanelOpacity("thevisitorPanel", setPanelPosition("thevisitorPanel", percent));
  }
  
}

function setPanelPosition(id, percent) {
  // needed to return to let other functions know where the control will be, post-animation
  var newPosition = {"percent":percent};
  
  if (typeof data.scale[id] !== "undefined") {
    
    var translateX = 0, translateY = 0, translateZ = 0;
    
    // move up/down
    if (typeof data.scale[id].y !== "undefined") {
      newPosition.y = window.innerHeight * data.scale[id].y * Math.easeHelper(percent);
      translateY =  newPosition.y;
    }

    // move left/right
    if (typeof data.scale[id].x !== "undefined") {
      newPosition.x = (window.innerWidth * data.scale[id].x * Math.easeHelper(percent));
      //newPosition.xOffset = data.maskSide.offsetWidth;
      translateX = newPosition.x;
    }
    
        // move int/out
    if (typeof data.scale[id].z !== "undefined") {
      newPosition.z = data.scale[id].z * Math.easeHelper(percent);
      translateZ = newPosition.z;
    }

    data.panels[id].style["-moz-transform"] = "translate3d(" + translateX + "px, " + translateY + "px, " + translateZ + "px)";
    data.panels[id].style["-webkit-transform"] = "translate3d(" + translateX + "px, " + translateY + "px, " + translateZ + "px)";
    data.panels[id].style["transform"] = "translate3d(" + translateX + "px, " + translateY + "px, " + translateZ + "px)";

  }
	console.log(id, newPosition);
  return newPosition;
}

// calculates the perentage of the item visible in the viewport
// percentage is offset by an easing function to make it less linear
function setPanelOpacity(id, newPosition) {
  
  var percent;
  if (typeof newPosition.y !== "undefined") {
    if (newPosition.y < 0) {
      percent = 1 + newPosition.y / (window.innerHeight);
    }
    else {
      percent = 1 - newPosition.y / (window.innerHeight);
    }
    opacityHelper(data.panels[id], Math.easeHelper(percent));
  } 
  else if (typeof newPosition.x !== "undefined") {
    if (newPosition.x < 0) {
      percent = 1 + newPosition.x / (window.innerWidth);
    }
    else {
      percent = 1 - newPosition.x / (window.innerWidth);
    }
    opacityHelper(data.panels[id], Math.easeHelper(percent));
  }
  else {
    opacityHelper(data.panels[id], Math.easeHelper(1 - newPosition.percent));
  }
}

function opacityHelper(obj, value) {
  obj.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (value * 100) + ")";
  obj.style.opacity = value;
}

// when the screen resizes, for the aspect ratio or else it gets truncated and looks dumb!
// image aspect ratio is 3:2 (width:height)
function fixAspectRatio() {

  var h = window.innerHeight, w = window.innerWidth;
  if (h > w) {
    data.assetWidth = w;
    data.assetHeight = window.innerWidth / 1.5;
  }
  else {
    data.assetHeight = h;
    data.assetWidth = data.assetHeight * 1.5;
  }
  for (var index = 0; index < data.resizeItems.length; index++) {
    data.resizeItems[index].style.height = data.assetHeight + "px";
    data.resizeItems[index].style.width = data.assetWidth + "px";
  }
  
  //apply maskSide to hide sieways animations
  data.maskSide.style.left = data.assetWidth + "px";
  data.maskBottom.style.top = data.assetHeight + "px";
  
  // resize canvas with attributes, because it currently warps if you use css width and height
  // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_usage#The_<canvas>_element
  if (data.canvas) {
    // resize of canvas that uses attribute rather than css for dimensions will wipe it! FFS.
    data.canvas.setAttribute("width", data.assetWidth + "px");
		data.canvas.setAttribute("height", data.assetHeight + "px");
    
    canvas.resetLights();
  }
  
  if (data.scrollHint) {
    data.scrollHint.style["padding-top"] = (data.assetHeight * 0.5) + "px";
  }
}

/********* Canvas object *********/
var canvas = {
  didYouHearThat: function() {
    setTimeout(function() {
      canvas.drawHouse("house1");
      setInterval(function() {
        canvas.drawHouse("house1");
      }, 10.1 * 1000);
    }, 5 * 1000);

    setTimeout(function() {
      canvas.drawHouse("house2");
      setInterval(function() {
        canvas.drawHouse("house2");
      }, 9.2 * 1000);
    }, 1 * 1000);

    setTimeout(function() {
      canvas.drawHouse("house3");
      setInterval(function() {
        canvas.drawHouse("house3");
      }, 11.3 * 1000);
    }, 3 * 1000);

  },

  // draws the house lights
  drawHouse : function(houseName, lightOverride) {

    var windows = data.houses[houseName].windows;
    var lights = lightOverride || data.houses[houseName].lights || "fill";

    for (var index = 0; index < windows.length; index++) {
      var item = windows[index];
      canvas.drawCanvas(item[0], item[1], item[2], item[3], lights);
    }
    if (lights === "clear") {
      data.houses[houseName].lights = "fill";
    }
    else {
      data.houses[houseName].lights = "clear";
    }
  },

  // canvas helper
  drawCanvas : function(left, top, width, height, _action, _color) {

    if (data.canvasContext) {

      if (_color != null) { 
        data.canvasContext.fillStyle = _color;//"rgb(0,200,200)";
      }

      if (_action === "clear") {
        data.canvasContext.clearRect(
          canvas.percentToWidth(left), 
          canvas.percentToHeight(top), 
          canvas.percentToWidth(width), 
          canvas.percentToHeight(height));
      }
      else {
        data.canvasContext.fillRect(
          canvas.percentToWidth(left), 
          canvas.percentToHeight(top), 
          canvas.percentToWidth(width), 
          canvas.percentToHeight(height));
      }
    }
  },
  
  percentToWidth : function(value) {
    return value / 100 * data.assetWidth;
  },
  
  percentToHeight : function(value) {
    return value / 100 * data.assetHeight;
  },
  
  resetLights : function() {
  	data.houses.house1.lights = "fill";
  	data.houses.house2.lights = "fill";
  	data.houses.house3.lights = "fill";
	}
}


/********* Math Functions *********/
// http://gizma.com/easing/

Math.easeInSine = function (t, b, c, d) {
	return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
};

Math.easeHelper = function (scrollPercentage) {
  // generate range 0 to 1 from percentage (0[t] to 1) scrolled.
  var outputMin = 0, outputMax = 1;
	var timeMax = 1;
	return Math.easeInSine(scrollPercentage, outputMin, outputMax, timeMax);
};

// Document Ready!
document.addEventListener("DOMContentLoaded", init);


            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console