Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <!-- Template Staff -->
<a class="info" target="blank" href="https://revolution.themepunch.com">by ThemePunch</a>
<title>Eye Catcher on the Fly</title>
<subtitle>MouseTrap Addon Mockup for Slider Revolution</subtitle>

<!-- The World -->
<world>
  <fly id="fly"><flybody></flybody><smallwings></smallwings><middlewings></middlewings><bigwings></bigwings><fly_eye class="left"></fly_eye><fly_eye class="right"></fly_eye></fly>
  <guy id="guy">
    <eyebrow_left></eyebrow_left>
    <eye id="left"><iris id="left_iris"></iris></eye>
    <eyebrow_right></eyebrow_right>
    <eye id="right"><iris id="right_iris"></iris></eye>
    <nose id="nose"></nose>
  </guy>
</world>

              
            
!

CSS

              
                world {
  display: block;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0px;
  left: 0px;
}

/* MR GUY */
guy {
  position: absolute;
  left: 50%;
  top: 50%;
  height: 200px;
  transform: translate(-50px, -50%);
}

/* EYE FROM THE GUY */
eye {
  border-width: 3px 3px 3px;
  border-color: #000;
  border-style: solid;
  background: #fff;
  display: block;
  border-radius: 50%;
  position: absolute;
  overflow: hidden;
}
eyebrow_left {
  width: 70px;
  height: 30px;
  border-radius: 150%;
  position: absolute;
  top: -20px;
  left: -30px;
  border-top: 5px solid #000;
  transform: rotate(14deg);
}
eyebrow_right {
  width: 70px;
  height: 30px;
  border-radius: 150%;
  position: absolute;
  top: -10px;
  left: 35px;
  border-top: 5px solid #000;
  transform: rotate(-25deg);
}
eye#left {
  left: -45px;
  width: 110px;
  height: 150px;
  transform: rotate(-20deg);
}
eye#right {
  left: 45px;
  width: 90px;
  height: 120px;
  transform: rotate(20deg);
}
iris {
  background: #000;
  border-radius: 50%;
  display: block;
  position: absolute;
  width: 40px;
  height: 52px;
  top: 40%;
  left: 20px;
}

/* NOSE FROM THE GUYS */
nose {
  border-width: 0px 4px 10px 4px;
  border-color: #000;
  border-style: solid;
  background: transparent;
  width: 140px;
  height: 120px;
  border-radius: 70%;
  position: absolute;
  top: 140px;
  left: 50%;
  transform: rotate(-25deg);
}

/* THE FLY */
fly {
  width: 59px;
  height: 72px;
  display: block;
  position: absolute;
  left: 50%;
  top: 80%;
  z-index: 100;
  pointer-events: none;
}

flybody {
  display: block;
  position: absolute;
  background: url(https://revolution.themepunch.com/codepen.io/fly2.png);
  background-size: contain;
  width: 100%;
  height: 100%;
  z-index: 1;
}

bigwings {
  display: block;
  width: 30px;
  height: 30px;
  border-radius: 50px 10px;
  background: rgba(255, 255, 255, 0.5);
  position: absolute;
  top: 5px;
  left: 40px;
  z-index: 0;
}

middlewings {
  display: block;
  width: 23px;
  height: 25px;
  border-radius: 50px 10px;
  background: rgba(255, 255, 255, 0.5);
  position: absolute;
  top: 5px;
  left: 25px;
  z-index: 0;
}

smallwings {
  display: block;
  width: 15px;
  height: 22px;
  border-radius: 50px 10px;
  background: rgba(255, 255, 255, 0.5);
  position: absolute;
  top: 20px;
  left: 45px;
  z-index: 0;
}
fly_eye {background:rgba(255,0,0,0.1); border-top:3px solid #000; border-radius:50%; position:absolute; display:block;z-index:12; box-sizing:border-box;}
fly_eye.left { width:16px;height:19px; left:2px; top:12px; }
fly_eye.right { width:22px;height:22px; left:12px; top:7px;}
/* 
Template Stuff
*/
html {
  background: #323436;
  color: #f1f1f1;
  font-family: Sans-Serif;
  text-align: center;
  padding: 10px;
}
title {
  font-size: 26px;
  line-height: 26px;
  color: #ecf0f1;
  position: relative;
  display: block;
}
.info {
  position: absolute;
  bottom: 0px;
  right: 0px;
  padding: 5px;
  color: #fff !important;
  text-decoration: none;
  background: rgb(102, 63, 181);
  z-index: 400;
}
.info:hover {
  filter: brightness(120%);
}
subtitle {
  color: #666;
  font-size: 13px;
}

              
            
!

JS

              
                // Define some Defaults
var RAD2DEG = 180 / Math.PI,
  DEG2RAD = Math.PI / 180,
  world = document.getElementsByTagName("world")[0],
   guy = document.getElementsByTagName('guy')[0],
  fly = {
    self: document.getElementById("fly"),
    body: document.getElementsByTagName("flybody")[0],
    smallwings: document.getElementsByTagName("smallwings")[0],
    middlewings: document.getElementsByTagName("middlewings")[0],
    bigwings: document.getElementsByTagName("bigwings")[0],
    eye: document.getElementsByTagName("fly_eye"),
    delay: 1500,
    ease: "cubic.out",
    radius: 0,
    revert: { use: true, delay: 400, ease: "back.out" },
    rotate: { use: true, delay: 600, ease: "none" },
    offset: { x: 38, y: 36 }
  },
  left = {
    self: document.getElementById("left_iris"),
    parent: document.getElementById("left"),
    delay: 1600,
    ease: "power3.Out",
    radius: 30,
    revert: { use: true, delay: 400, ease: "back.out" },
    rotate: { use: true, delay: 1500, ease: "quad.out" },
    offset: { x: 20, y: 26 }
  },
  right = {
    self: document.getElementById("right_iris"),
    parent: document.getElementById("right"),
    delay: 1300,
    ease: "power2.Out",
    radius: 25,
    revert: { use: true, delay: 400, ease: "back.Out" },
    rotate: { use: true, delay: 1200, ease: "quad.out" },
    offset: { x: 17, y: 30 }
  };

//Reset some Coordinates and Sizes
reset();
animateFly();

function animateFly() {
  var tl = gsap.timeline({ repeat: -1, yoyo: true });
  tl.fromTo(
    fly.bigwings,
    0.1,
    { rotationZ: "-25deg", transformOrigin: "50% 100%" },
    { rotationZ: "5deg" },
    0
  );
  tl.fromTo(
    fly.smallwings,
    0.1,
    { rotationZ: "25deg", transformOrigin: "50% 100%" },
    { rotationZ: "35deg" },
    0
  );
  tl.fromTo(
    fly.middlewings,
    0.1,
    { rotationZ: "-15deg", transformOrigin: "50% 100%" },
    { rotationZ: "-40deg" },
    0
  );
  fly.eyetl = gsap.timeline({ yoyo: true });
  fly.eyetl.add(gsap.to(fly.eye, 0.2, { 'borderTopWidth': 20 }));
  fly.eyetl.add(
    gsap.to(fly.eye, 0.2, {
      'borderTopWidth': 2,
      onComplete: function() {
        gsap.delayedCall(Math.random() * 3, function() {
          fly.eyetl.play(0);
        });
      }
    })
  );
}

//Add Those Test Values to dat.GUI
prepareTestEnvironment();

//On Resize recalculate Size and Position
window.addEventListener("resize", reset);

//Start Listeners
world.addEventListener("mousemove", e => {
  animate(fly, { x: e.clientX, y: e.clientY });
  animate(left, { x: e.clientX, y: e.clientY });
  animate(right, { x: e.clientX, y: e.clientY });
});
world.addEventListener("mouseleave", e => {
  if (fly.revert.use) revert(fly);
  if (left.revert.use) revert(left);
  if (right.revert.use) revert(right);
});

function reset() {
  resetCoordinates(fly);
  resetCoordinates(left);
  resetCoordinates(right);
}

function resetCoordinates(obj) {
  var box = obj.self.getBoundingClientRect();
  obj.original = { x: box.left, y: box.y };
}

function animate(obj, mouse) {
  // Calculate Offseted Coordinates
  var x = mouse.x - obj.offset.x,
    y = mouse.y + document.documentElement.scrollTop - obj.offset.y,
      delta = getRelativePosition(fly.self, guy, [0.5, 0.5], [0.5, 0.5]),
      angry = 0.7- Math.max(Math.abs(gsap.utils.normalize(0,window.window.outerWidth/2,(delta.x+delta.y)/2)),0);
  
  //Get Rotation and new Position
  obj.current = getMath({
    mouse: { x: x, y: y },
    original: obj.original,
    last: obj.current || { x: 0, y: 0, rotation: 0 },
    radius: obj.radius,
    rotate: obj.rotate
  });
  // Get Red Eye for Fly if getting closer
  gsap.set(fly.eye,{backgroundColor:'rgba(255,0,0,'+angry+')'});
  //Animate to Position
  gsap.to(obj.self, {
    duration: obj.delay / 1000,
    x: obj.current.x,
    y: obj.current.y,
    overwrite: "auto",
    ease: obj.ease,
    transformOrigin: obj.offset.x + "px " + obj.offset.y + "px"
  });

  //If Rotation Set, Rotate Element
  if (obj.rotate.use)
    gsap.to(obj.self, {
      duration: obj.rotate.delay / 1000,
      ease: obj.rotate.ease,
      rotation: obj.current.rotation + "_short",
      overwrite: "auto"
    });
}

//Revert things to original Position
function revert(obj) {
  gsap.to(obj.self, obj.revert.delay / 1000, {
    x: 0,
    y: 0,
    overwrite: "auto",
    ease: obj.revert.ease
  });
}

// Render Rotation and Position
function getMath(_) {
  var x, y, flip, alpa;
  // Get Position (Respect Wrapper Offsets)
  _.new = {
    x: _.mouse.x,
    y: _.mouse.y,
    rotation: _.last.rotation || 0
  };

  // Set Position Limits if Radius is set
  if (_.radius > 0) {
    //Use Pitagoras and calculate original Angle
    x = _.new.x - _.original.x;
    y = _.new.y - _.original.y;

    //If Radius is bigger then predefined Radius, calculate new Positions
    if (Math.sqrt(x * x + y * y) > _.radius && x !== 0 && y !== 0) {
      flip = _.new.x < _.original.x ? -1 : 1;
      alpha = Math.atan(y / x);
      //calculate x,y coordinates baed on max Radius and Angle
      _.new.x = Math.cos(alpha) * _.radius * flip;
      _.new.y = Math.sin(alpha) * _.radius * flip;
    } else {
      //Check Radius if Angle is 0,90,180,270 degree (x==0 || y==0)
      _.new.x = x > _.radius ? _.radius : x < 0 - _.radius ? 0 - _.radius : x;
      _.new.y = y > _.radius ? _.radius : y < 0 - _.radius ? 0 - _.radius : y;
    }
  } else {
    _.new.x = _.new.x - _.original.x;
    _.new.y = _.new.y - _.original.y;
  }

  // If rotation used, calculate direction Angle
  if (_.rotate.use) {
    x = _.last.x - _.new.x;
    y = _.last.y - _.new.y;
    _.new.rotation =
      Math.sqrt(x * x + y * y) > 2 // 2 is just try and error. This feels well, but maybe someone has a better idea here :=)
        ? Math.round(Math.atan2(y, x) * RAD2DEG) - 90 || _.new.rotation
        : _.new.rotation;
  }

  //Something Changed, for further ideas...
  _.new.move = _.new.x - _.last.x !== 0 || _.new.y - _.last.y !== 0;
  return _.new;
}

//Prepare the Test Values and call that dat.GUI  (Not that interesting from here...)
function addSingleGui(gui, name, obj, eases) {
  var guiBasic = gui.addFolder(name + " Basics");
  guiRotate = gui.addFolder(name + " Rotation");
  guiRevert = gui.addFolder(name + " Revert");

  guiBasic.add(obj, "delay", 0, 5000);
  guiBasic.add(obj, "ease", eases);
  guiRotate.add(obj.rotate, "use");
  guiRotate.add(obj.rotate, "delay", 0, 5000);
  guiRotate.add(obj.rotate, "ease", eases);
  guiRevert.add(obj.revert, "use");
  guiRevert.add(obj.revert, "delay", 0, 5000);
  guiRevert.add(obj.revert, "ease", eases);
}

function prepareTestEnvironment() {
  var eases = ["none"],
    gui = new dat.GUI();

  // Collect all Eases
  for (var ease in gsap.parseEase())
    if (ease.indexOf(".") > 0 && ease.indexOf(".ease") === -1) eases.push(ease);

  addSingleGui(gui, "Fly", fly, eases);
  addSingleGui(gui, "Left Eye", left, eases);
  addSingleGui(gui, "Right Eye", right, eases);
}


// feed two elements to this method and it'll return the gap between them as a point {x, y} according to the coordinate system of the fromElement's parent. By default, it will align their centers, but you can define a different origin for each, like [0, 0] would be the top left corner, [0.5, 0.5] would be the center, [1, 1] would be the bottom right, etc. 
function getRelativePosition(fromElement, toElement, fromOrigin, toOrigin) {
	let matrix = MotionPathPlugin.getGlobalMatrix,
		originToPoint = (element, origin) => {
			let o = origin || [0.5, 0.5],
				svg = element.ownerSVGElement && element.getBBox(),
				w = svg ? svg.width : element.offsetWidth || 0,
				h = svg ? svg.height : element.offsetHeight || 0;
			return {x: o[0] * w, y: o[1] * h};
		},
		parentMatrix = matrix(fromElement.parentNode, true),
		fromPoint = parentMatrix.apply(matrix(fromElement).apply(originToPoint(fromElement, fromOrigin))),
		toPoint = parentMatrix.apply(matrix(toElement).apply(originToPoint(toElement, toOrigin)));
	return {x: toPoint.x - fromPoint.x, y: toPoint.y - fromPoint.y};
}
              
            
!
999px

Console