Skip to content

Instantly share code, notes, and snippets.

@laura-dumitru
Last active March 15, 2024 15:36
Show Gist options
  • Save laura-dumitru/6a2638c4976ebb40e225a84a32c423cf to your computer and use it in GitHub Desktop.
Save laura-dumitru/6a2638c4976ebb40e225a84a32c423cf to your computer and use it in GitHub Desktop.
Tap / Reveal Image - built while working at Mobkoi.

Example of a mobile ad built using the Tap / Reveal Image template.

//Options for designers to customize template.
let options = {
hotspots: {
hs1: ['Img1', 'cta1'],
hs2: ['Img2', 'cta2'],
hs3: ['Img3', 'cta3'],
},
stickyImage: true, // follows the user's finger
staticImage: false, //stops on the screen when the user lets go, doesn't follow finger.
hideImage: true, // shows the image after the finger leaves the hotspot. If sticky, this option should stay true.
transition: '0.5s', //duration in seconds that it takes for the change from one image to another.
offsetX: 80, // horizontal offset - value in pixels.It determines how far to the left or right of your thumb the image will appear.
offsetY: 60, //vertical offset - value in pixels. It determines how far above or below your thumb the image will appear.
goToPage: 'Page2', // match the name of the page layer it transitions to.
fadeDuration: 750, // the time (in ms) it takes for page1 to transition to page2.
animationType: 'slide', // can be fade, slide or none.
direction: 'south', // can be north, south, east or west.
video: false, // set to true if you want to use the video functionality.
};
// Please do not change the code below this line.
/////////////////////////////////////////////////////////
// Action Context
let flags = [];
let customCtx = ctx;
screen.once(
'appeared',
function () {
customCtx = new ActionContext(this, {
certainlyNotCausedByUserBehavior: false,
consideredUserInitiatedByBrowser: false,
});
init();
}.bind(this),
);
// Log Custom events
function mediator(event_name) {
if (!flags.includes(event_name)) {
Creative.trackCustomEventAction(customCtx, { name: event_name }, function () {
flags.push(event_name);
console.debug('mediator', event_name);
console.debug(flags);
});
}
}
const regex = /Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
const mobile = regex.test(navigator.userAgent);
let moving = false;
let x, y;
let image;
let lastTouchedCta;
const imagesGroup = screen.find('Images');
const hotspotsGroup = screen.find('Hotspots');
const hotspotsArray = hotspotsGroup.content.objects;
const imagesArray = imagesGroup.content.objects;
const ctaGroup = screen.find('ctaGroup');
const ctaArray = ctaGroup.content.objects;
const video = screen.find('vidPlayer');
const arrow = screen.find('nextArrow') || screen.find('previousArrow');
function init() {
// Loop over imagesGroup and hide each one with opacity 0
function hideImages() {
imagesArray.forEach((image) => image.setOpacity(0));
}
// Loop over all cta layers and hide when the ad loads
function hideCtas() {
ctaArray.forEach((cta) => cta.hideAction(ctx, {}, noop));
}
hideCtas();
function showLastCta() {
if (lastTouchedCta) {
lastTouchedCta.showAction(ctx, {}, noop); // show the last touched CTA on touchend
}
}
function followFinger(event) {
event.preventDefault();
if (mobile) {
// For mobile, use touch event coordinates
x = event.touches[0].clientX;
y = event.touches[0].clientY;
} else {
// For desktop, use mouse event coordinates
x = event.clientX;
y = event.clientY;
}
// Position the image to the centre
function positionImage(image, x, y) {
const xOffset = x - options.offsetX;
const yOffset = y - options.offsetY;
// Calculate the position relative to the hotspot
image.node.style.top = `calc(${yOffset}px - ${image.node.style.height} / 2)`;
image.node.style.left = `calc(${xOffset}px - ${image.node.style.width} / 2)`;
}
if (moving) {
// if the user is touching or hovering the screen
hotspotsArray
.filter((hotspot) => hotspot instanceof Hotspot) // filtering out any layers that are not hotspots
.forEach((hotspot) => {
const hotspotsValues = options.hotspots[hotspot.name];
image = screen.find(hotspotsValues[0]);
image.node.style.transition = `opacity ${options.transition}`;
// Calculating whether the finger is inside the boundaries of the hotspot
const { left, right, top, bottom } = hotspot.node.getBoundingClientRect();
if (x >= left && x <= right && y >= top && y <= bottom) {
if (options.video) {
video.pauseAction(ctx, {}, noop);
mediator(`Video paused`);
}
// if I am in the boundaries of the box set the opacity of the image to 1
image.setOpacity(1);
lastTouchedCta = screen.find(hotspotsValues[1]);
mediator(`User viewed image ${hotspotsValues[0]}`);
} else if (options.hideImage) {
//console.log("outside");
image.setOpacity(0); // when finger no longer in the box hide image
}
if (options.stickyImage) {
positionImage(image, x, y); //options.transition
}
});
}
}
// add touchstart and touchend event listeners for mobile
if (mobile) {
document.addEventListener('touchmove', followFinger);
hotspotsGroup.node.addEventListener('touchstart', (event) => {
event.preventDefault();
moving = true;
followFinger(event);
});
hotspotsGroup.node.addEventListener('touchend', (event) => {
event.preventDefault();
moving = false;
if (options.video) {
video.playAction(ctx, {}, noop);
mediator(`Video playing`);
}
if (options.stickyImage || (options.staticImage && options.hideImage)) {
imagesArray.forEach((image) => {
image.node.style.transition = `opacity ${options.transition}`;
image.setOpacity(0); // when finger no longer in the box hide image
// Keep the image at its original size (no scaling)
image.node.style.transform = 'scale(1)';
});
hideImages();
imagesArray.forEach((image) => {
if (options.stickyImage) {
image.node.style.transition = ''; // Remove CSS transition (fade)
// TOOD this hides the 'snapping' back
}
image.setOpacity(0); // when finger no longer in the box hide image
});
image = null; // reset
}
hideCtas();
showLastCta();
});
// add mousemove (hover) event listener for desktop
} else {
document.addEventListener('mousemove', followFinger);
hotspotsGroup.node.addEventListener('mousemove', (event) => {
//console.log("mousemove")
moving = true;
followFinger(event);
hideCtas();
showLastCta();
});
// adding mouseout even listener and looping over the contents of the hotspot group to play the video
if (options.video) {
hotspotsArray.forEach((hotspot) => {
hotspot.node.addEventListener('mouseout', () => video.playAction(ctx, {}, noop));
mediator(`Video playing`);
});
}
}
if (arrow) {
arrow.node.addEventListener('click', (event) => {
const page = unit.find(options.goToPage);
unit.goToScreenAction(
ctx,
{
screen: page,
animation: { animation: options.animationType, duration: options.fadeDuration, direction: options.direction },
},
noop,
);
});
}
}
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();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment