Skip to content

Instantly share code, notes, and snippets.

@laura-dumitru
Last active March 15, 2024 14:44
Show Gist options
  • Save laura-dumitru/fe54a7a923eb3edc36f075db9c5a20ed to your computer and use it in GitHub Desktop.
Save laura-dumitru/fe54a7a923eb3edc36f075db9c5a20ed to your computer and use it in GitHub Desktop.
Spotlight template - built while working at Mobkoi.

Example of a few mobile ads built for Hugo Boss, Chanel and Jaguar using the Spotlight template.

<div class="overlay">
</div>
// Options for creative team to modify
const options = {
overlayColor: "black", // change the overlay/shadow background colour- you can use any hex colour value
overlayOpacity: "80%", // change the opacity of the black overlay
spotlightSize: "24vw", // change the width of the spotlight. Increase to maximum 26vw - details in the Wiki
softEdge: "80%", // change the spotlight edge softness.The lower the percentge, the softer the edge of the spotlight.
circlePulse: true, // true will load the pulse animation,false will turn it off- this is valid only for the pulse animation when the ad loads
initialAnimation: true, // true plays the initial animation, false turns it off
initialAnimationDuration: 0.5, // this option refers to how long the initial animation lasts
initialAnimationEase: "slow(0.2, 0.2, false)", //Please see wiki for further explanation
pulseTimeout: 2000, // number of millseconds before the spotlight starts to pulse once the user stops touching the screen
pulseIn: "26vw", // controls the pulse animation.Should be 2 points above spotlightSize.Check wiki for further explanation
pulseTime: 0.25, // time in seconds.This refers to how fast or slow the pulse animation pulses
pulseEase: "slow(0.2, 0.3, false)", //Please see wiki for further explanation
OFFbutton: true, // set to false if you do not want a spotlight button
ONbutton: true, // set to false if the on button is not needed
userNoInteraction: 10000, //ms number of milliseconds before the spotlight turns off if there is no user interaction
};
///// Please do not change code below this line! -------------------------------------------------------------------------------------------------------------------
const version = "3.11.5"; // GSAP
let flags = [];
let hasLoaded = false;
let actionCtx = ctx;
screen.once(
"appeared",
function () {
actionCtx = new ActionContext(this, {
certainlyNotCausedByUserBehavior: false,
consideredUserInitiatedByBrowser: false,
});
loadJS(
`https://cdnjs.cloudflare.com/ajax/libs/gsap/${version}/gsap.min.js`,
init
);
}.bind(this)
);
function mediator(event_name) {
if (!flags.includes(event_name)) {
Creative.trackCustomEventAction(
actionCtx,
{ name: event_name },
function () {
flags.push(event_name);
console.debug("mediator", event_name);
console.debug(flags);
}
);
}
}
function init() {
const overlay = div.querySelector(".overlay");
mediator("Spotlight_on");
const off = screen.find("off");
const on = screen.find("on");
// Initial overlay properties
overlay.style.setProperty("--overlay-opacity", options.overlayOpacity);
// Initial spotlight properties
overlay.style.setProperty("--spotlight-width", options.spotlightSize);
overlay.style.setProperty("--spotlight-height", options.spotlightSize);
overlay.style.setProperty("--spotlight-soft-edge", options.softEdge);
overlay.style.setProperty("--overlay-color", options.overlayColor);
overlay.style.setProperty("--mouse-x", "66vw");
overlay.style.setProperty("--mouse-y", "20vh");
const timeline = gsap.timeline();
Ticker.frame(() => {
if (unit.inView.areaInViewRatio >= 0.7 && hasLoaded === false) {
if (options.initialAnimation) initialAnimation();
if (options.circlePulse) pulse();
hasLoaded = true;
timeline.to(".overlay", { onComplete });
}
});
// Animation spotlight
function initialAnimation() {
if (div.clientHeight <= Math.ceil(unit.size.height / 2)) {
timeline.to(".overlay", { "--mouse-x": "33vw", "--mouse-y": "25vh", duration: options.initialAnimationDuration }); //, delay: options.initialDelay });
timeline.to(".overlay", { "--mouse-x": "66vw", "--mouse-y": "30vh", duration: options.initialAnimationDuration, ease: options.initialAnimationEase });
timeline.to(".overlay", { "--mouse-x": "33vw", "--mouse-y": "35vh", duration: options.initialAnimationDuration, ease: options.initialAnimationEase });
} else {
timeline.to(".overlay", { "--mouse-x": "33vw", "--mouse-y": "30vh", duration: options.initialAnimationDuration, delay: options.initialDelay });
timeline.to(".overlay", { "--mouse-x": "66vw", "--mouse-y": "40vh", duration: options.initialAnimationDuration, ease: options.initialAnimationEase });
timeline.to(".overlay", { "--mouse-x": "33vw", "--mouse-y": "50vh", duration: options.initialAnimationDuration, ease: options.initialAnimationEase });
}
}
// Pulse spotlight
function pulse() {
//let delay;
//if (options.initialAnimation === false) delay = options.initialDelay;
timeline.to(".overlay", {"--spotlight-width": options.pulseIn, "--spotlight-height": options.pulseIn, duration: options.pulseTime }); //, delay });
timeline.to(".overlay", {"--spotlight-width": options.spotlightSize, "--spotlight-height": options.spotlightSize, duration: options.pulseTime, ease: options.pulseEase });
timeline.to(".overlay", {"--spotlight-width": options.pulseIn, "--spotlight-height": options.pulseIn,duration: options.pulseTime, ease: options.pulseEase });
timeline.to(".overlay", {"--spotlight-width": options.spotlightSize, "--spotlight-height": options.spotlightSize, duration: options.pulseTime, ease: options.pulseEase });
}
function onComplete() {
overlay.addEventListener("pointermove", pointerMove);
}
overlay.addEventListener("touchstart", touchStart);
overlay.addEventListener("touchend", touchEnd);
let pulseTimeout, userInteraction, userNoInteraction; //pulseInterval;
function touchStart(e) {
e.preventDefault();
clearTimeout(pulseTimeout); //clearInterval(pulseInterval);
}
function touchEnd() {
pulseTimeout = setTimeout(pulse, options.pulseTimeout); //setInterval(pulse, options.pulseTimeout);
}
userNoInteraction = setTimeout(() => {
mediator("User didn't touch the screen for 10 seconds");
overlay.style.setProperty("transition", "0.8s ease-in-out");
overlay.style.opacity = "0";
clearTimeout(userNoInteraction);
}, options.userNoInteraction);
function pointerMove(e) {
clearTimeout(userNoInteraction);
userInteraction = setTimeout(() => {
mediator("User interacted more than 3 seconds");
clearTimeout(userInteraction); // if the event is triggered again before 3 seconds, clear the timer
}, 3000); // start a timer for 3 seconds
const x = e.offsetX; // subtract 50 from the x-coordinate to make the finger/mouse position not cover the spotlight
const y = e.offsetY; // subtract 50 from the y-coordinate to make the finger/mouse position not cover the spotlight
overlay.style.setProperty("--mouse-x", `${x}px`);
overlay.style.setProperty("--mouse-y", `${y - 64}px`); //options.spotlightSize / 2}px`);
const edges = gsap.timeline();
// Check if mouse is at edge of screen
if (x <= 3 || y <= -3 || x >= div.clientWidth - 3 || y >= div.clientHeight - 3) {
edges.to(".overlay", {"--spotlight-width": 0, "--spotlight-height": 0, duration: 0.25, ease: "low(0.5, 0.5, false)" });
} else {
edges.to(".overlay", {"--spotlight-width": options.spotlightSize,"--spotlight-height": options.spotlightSize,duration: 0.25 });
}
}
if (options.OFFbutton) {
off.node.addEventListener("click", () => {
mediator("User turned spotlight off");
overlay.style.display = "none";
});
}
if (options.ONbutton) {
on.node.addEventListener("click", () => {
mediator("User turned spotlight on");
overlay.style.removeProperty("transition");
overlay.style.opacity = options.overlayOpacity;
overlay.style.display = "inherit";
});
}
}
function logger() {
if (window.location.href.indexOf("celtra") != -1) {
let date = new Date();
let now =
date.getFullYear() +
"-" +
(date.getMonth() + 1) +
"-" +
date.getDate() +
" " +
date.getHours() +
":" +
date.getMinutes() +
":" +
date.getSeconds();
console.debug(
"%c-------LOGGER OUTPUT @ " + now + "-------------",
"background: navy; color: #39FF14"
);
Array.from(arguments).map(function (m) {
console.debug(m);
});
}
}
c();
:root {
--overlay-color: "black";
--overlay-opacity: 80%;
--spotlight-width: 80px;
--spotlight-height: var(--spotlight-width);
--spotlight-soft-edge: 80%;
--mouse-x: 50%;
--mouse-y: 50%;
}
.overlay {
width: 100%;
height: 100%;
background-size: cover;
background-position: center center;
background-color: var(--overlay-color);
opacity: var(--overlay-opacity);
/*mask: radial-gradient(var(--mouse-x) var(--mouse-y), var(--spotlight-width) var(--spotlight-height), transparent 0, black);*/
-webkit-mask: -webkit-radial-gradient(var(--mouse-x) var(--mouse-y), var(--spotlight-width) var(--spotlight-height), transparent var(--spotlight-soft-edge), black);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment