I have an API list and I want my divs to float within this huge container. this is my reference - but for some reason this doesn't work for mine (http://jsfiddle.net/bsd3b0r0/1/) I know I'm probably missing something, naming of my divs or something but I simply cannot figure it out. Would appreciate any advice.
/* globals require */
console.log("Hello, Airtable");
let wrapper = document.querySelector(".container");
// load the airtable library, call it "Airtable"
let Airtable = require("airtable");
console.log(Airtable);
// use the airtable library, connect to our base using API key
let base = new Airtable({ apiKey: "keyXfgdNIcv7dW63l" }).base(
"appWiQM0htfVBj9RL"
);
//get the "books" table from the base, select ALL the records, and specify the functions that will receive the data
base("nature_sounds").select({}).eachPage(gotPageOfSounds, gotAllSounds);
// an empty array to hold our book data
let sounds = [];
// callback function that receives our data
function gotPageOfSounds(records, fetchNextPage) {
console.log("gotPageOfSounds()");
// add the records from this page to our books array
sounds.push(...records);
// request more pages
fetchNextPage();
}
// call back function that is called when all pages are loaded
function gotAllSounds(err) {
console.log("gotAllSounds()");
// report an error, you'd want to do something better than this in production
if (err) {
console.log("error loading sounds");
console.error(err);
return;
}
// call functions to log and show the books
consoleLogSounds();
showSounds();
}
// just loop through the books and console.log them
function consoleLogSounds() {
console.log("consoleLogSounds()");
sounds.forEach((sound) => {
console.log("Sound:", sound);
});
}
// loop through the books, create an h2 for each one, and add it to the page
function showSounds() {
console.log("showSounds()");
sounds.forEach((sound) => {
let soundTextHolder = document.createElement("div"); // victoria u changed it from h2 to div dont forget//
soundTextHolder.classList.add("entry");
soundTextHolder.innerText = sound.fields.emotion;
wrapper.appendChild(soundTextHolder);
let soundColor = sound.fields.color_hex_code;
soundTextHolder.style.backgroundColor = soundColor;
let audioHolder = document.createElement("audio");
audioHolder.src = sound.fields.audio_file[0].url;
audioHolder.classList.add("soundClass");
audioHolder.controls = true;
audioHolder.autoplay = true;
audioHolder.loop = true;
soundTextHolder.appendChild(audioHolder);
});
}
// trying to aniamte//
var divsize = 5,
divcount = 50;
var gRows = Math.floor($(".container").innerWidth() / divsize);
var gCols = Math.floor($('.container').innerHeight() / divsize);
var vals = _.shuffle(_.range(divcount));
var xpos = _.shuffle(_.range(gRows));
var ypos = _.shuffle(_.range(gCols));
_.each(vals, function(d, i) {
var $newdiv = $('<div/>').addClass("div");
$newdiv.css({
'position': 'absolute',
'left': (xpos[i] * divsize) + 'px',
'top': (ypos[i] * divsize) + 'px'
}).appendTo('.container').html(d);
animateDiv();
});
function newPosition() {
// Get viewport dimensions (remove the dimension of the div)
var h = $('.container').height() - 50;
var w = $('.container').width() - 50;
var newh = Math.floor(Math.random() * h);
var neww = Math.floor(Math.random() * w);
return [newh, neww];
}
function animateDiv() {
var newp = newPosition();
var oldp = $('div').offset();
var speed = 3000;
$('div').animate({
top: newp[0],
left: newp[1]
}, speed, function() {
animateDiv();
});
};
// // // collect all the divs
// var divs = document.getElementsByTagName('h2');
// // // get window width and height
// / var winWidth = window.innerWidth;
// / var winHeight = window.innerHeight;
// // // i stands for "index". you could also call this banana or haircut. it's a variable
// // for ( var i=0; i < divs.length; i++ ) {
// // // shortcut! the current div in the list
// // var thisDiv = divs[i];
// // // get random numbers for each element
// // randomTop = getRandomNumber(0, winHeight);
// // randomLeft = getRandomNumber(0, winWidth);
// // // update top and left position
// / thisDiv.style.top = randomTop +"px";
// / thisDiv.style.left = randomLeft +"px";
// }
// // function that returns a random number between a min and max
// function getRandomNumber(min, max) {
// return Math.random() * (max - min) + min;
// }
body{
font-family: Space Mono;
background-color: white;
}
h1{
text-align: right;
color: black;
font-family: sans-serif;
}
.container{
width: 100%;
/* align-items: center;
display: grid;
grid-row: inherit;
position: relative; */
}
div{
border-radius: 50% 20% / 10% 40%;
/* border-radius: 10% / 50%; */
width: 30vw;
margin-left: auto;
margin-right: auto;
text-align: center;
/* display: flex; */
flex-direction: column;
box-shadow: inset 0 0 2000px rgba(255, 255, 255, .5);
filter: blur(1px);
/* animation-name: floating;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out; */
}
/* #keyframes floating {
0% { transform: translate(0, 0px); }
50% { transform: translate(0, 15px); }
100% { transform: translate(0, -0px); }
} */
.entry1{
width: 60vw;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EllasticCollection</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>sound and color through emotion</h1>
<div id="myData" class="container">
<div class="individual-colors"></div>
</div>
<script src="airtable.browser.js" defer></script>
<script src="script.js" defer></script>
</body>
</html>
Related
I have a container that is expanded and collapsed on click of chevron icon. The code to collapse/expand the container is in the function transformAnimation. The code of transformAnimation is similar to the code on MDN web docs for requestAnimationFrame. The code to animate (scale) the container has been developed on the guidelines of this article on Building performant expand & collapse animations on Chrome Developers website.
I am not able to figure out how to calculate yScale value (which is nothing but y scale values for collapse/expand animation) as a function of the time elapsed since the start of the animation.
To elaborate what I mean, let's assume that the container is in expanded state. In this state the scaleY value of the container is 6. Now when user clicks on the toggle button, in the transformAnimation function for each animation frame, i.e, execution of the requestAnimationFrame callback step function, the value of scaleY should decrease from 6 (the expanded state) to 1 (the collapsed state) in the exact duration that I want the animation to run for.
In the present state, the code to calculate yScale is not working as expected.
const dragExpandableContainer = document.querySelector('.drag-expandable-container');
const dragExpandableContents = document.querySelector('.drag-expandable__contents');
const resizeableControlEl = document.querySelector('.drag-expandable__resize-control');
const content = document.querySelector(`.content`);
const toggleEl = document.querySelector(`.toggle`);
const collapsedHeight = calculateCollapsedHeight();
/* This height is used as the basis for calculating all the scales for the component.
* It acts as proxy for collapsed state.
*/
dragExpandableContainer.style.height = `${collapsedHeight}px`;
// Apply iniial transform to expand
dragExpandableContainer.style.transformOrigin = 'bottom left';
dragExpandableContainer.style.transform = `scale(1, 10)`;
// Apply iniial reverse transform on the contents
dragExpandableContents.style.transformOrigin = 'bottom left';
dragExpandableContents.style.transform = `scale(1, calc(1/10))`;
let isOpen = true;
const togglePopup = () => {
if (isOpen) {
collapsedAnimation();
toggleEl.classList.remove('toggle-open');
isOpen = false;
} else {
expandAnimation();
toggleEl.classList.add('toggle-open');
isOpen = true
};
};
function calculateCollapsedHeight() {
const collapsedHeight = content.offsetHeight + resizeableControlEl.offsetHeight;
return collapsedHeight;
}
const calculateCollapsedScale = function() {
const collapsedHeight = calculateCollapsedHeight();
const expandedHeight = dragExpandableContainer.getBoundingClientRect().height;
return {
/* Since we are not dealing with scaling on X axis, we keep it 1.
* It can be inverse to if required */
x: 1,
y: expandedHeight / collapsedHeight,
};
};
const calculateExpandScale = function() {
const collapsedHeight = calculateCollapsedHeight();
const expandedHeight = 100;
return {
x: 1,
y: expandedHeight / collapsedHeight,
};
};
function expandAnimation() {
const {
x,
y
} = calculateExpandScale();
transformAnimation('expand', {
x,
y
});
}
function collapsedAnimation() {
const {
x,
y
} = calculateCollapsedScale();
transformAnimation('collapse', {
x,
y
});
}
function transformAnimation(animationType, scale) {
let start, previousTimeStamp;
let done = false;
function step(timestamp) {
if (start === undefined) {
start = timestamp;
}
const elapsed = timestamp - start;
if (previousTimeStamp !== timestamp) {
const count = Math.min(0.1 * elapsed, 200);
//console.log('count', count);
let yScale;
if (animationType === 'expand') {
yScale = (scale.y / 100) * count;
} else yScale = scale.y - (scale.y / 100) * count;
//console.log('yScale', yScale);
if (yScale < 1) yScale = 1;
dragExpandableContainer.style.transformOrigin = 'bottom left';
dragExpandableContainer.style.transform = `scale(${scale.x}, ${yScale})`;
const inverseXScale = 1;
const inverseYScale = 1 / yScale;
dragExpandableContents.style.transformOrigin = 'bottom left';
dragExpandableContents.style.transform = `scale(${inverseXScale}, ${inverseYScale})`;
if (count === 200) done = true;
//console.log('elapsed', elapsed);
if (elapsed < 1000) {
// Stop the animation after 2 seconds
previousTimeStamp = timestamp;
if (!done) requestAnimationFrame(step);
}
}
}
requestAnimationFrame(step);
}
.drag-expandable-container {
position: absolute;
bottom: 0px;
display: block;
overflow: hidden;
width: 100%;
background-color: #f3f7f7;
}
.drag-expandable__contents {
height: 0;
}
.toggle {
position: absolute;
top: 2px;
right: 15px;
height: 10px;
width: 10px;
transition: transform 0.2s linear;
}
.toggle-open {
transform: rotate(180deg);
}
.drag-expandable__resize-control {
background-color: #e7eeef;
}
.burger-icon {
width: 12px;
margin: 0 auto;
padding: 2px 0;
}
.burger-icon__line {
height: 1px;
background-color: #738F93;
margin: 2px 0;
}
.drag-expandable__resize-control:hover {
border-top: 1px solid #4caf50;
cursor: ns-resize;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css.css">
</head>
<body>
<div class="drag-expandable-container">
<div class="drag-expandable__contents">
<div class="drag-expandable__resize-control">
<div class="burger-icon">
<div class="burger-icon__line"></div>
<div class="burger-icon__line"></div>
<div class="burger-icon__line"></div>
</div>
</div>
<div class="content" />
<div>
<div class="toggle toggle-open" onclick="togglePopup()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by #fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M416 352c-8.188 0-16.38-3.125-22.62-9.375L224 173.3l-169.4 169.4c-12.5 12.5-32.75 12.5-45.25 0s-12.5-32.75 0-45.25l192-192c12.5-12.5 32.75-12.5 45.25 0l192 192c12.5 12.5 12.5 32.75 0 45.25C432.4 348.9 424.2 352 416 352z"/></svg>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript" src="js.js"></script>
</html>
Am trying to do an Etch-A-Sketch project and currently hit some roadblocks. Currently am able to create a grid based off user input on prompt and clear the grid through a button.
The problem I'm facing now is in the function colorChange(), where I'm trying to get opacity of the background color to increase every time a mouse hover event is captured over the div. Everything works fine but opacity maxes out at 0.9 in the current code.
If instead using if (rgbaValue <= 0.9) or (rgbaValue < 1), the opacity value resets to 0.1 on the next hover after 0.9. How would I go about getting the opacity to 1 without resetting?
Code below:
html
<!DOCTYPE html>
<html>
<link rel="stylesheet" href="style.css">
<title>Etch A Sketch</title>
<body>
<div>
<button type="button" onclick="clearGrid()">Click to clear grid</button>
</div>
<div id="container"></div>
<script src="java.js"></script>
</body>
</html>
javascript
let gridSize = 0;
let gridWidth = 0;
gridSize = window.prompt("Please input the amount of squares per side of grid that you want created");
gridWidth = 100/gridSize;
createGrid();
colorChange();
function createSquare() {
const mainBody = document.querySelector("#container");
const gridMake = document.createElement("div");
gridMake.classList.add("squaregrid");
gridMake.style.width = gridWidth + "%";
gridMake.style.paddingBottom = gridWidth + "%";
mainBody.appendChild(gridMake);
}
function createGrid() {
gridCount = 0;
while (gridCount < (gridSize*gridSize)) {
createSquare();
gridCount++
}
}
function colorChange() {
const selectSquare = document.querySelectorAll(".squaregrid");
selectSquare.forEach(square => square.addEventListener("mouseover", event => {
square.classList.add('hover-change');
const style = getComputedStyle(square);
let styleDeep = style.backgroundColor;
rgbaValue = parseFloat(styleDeep.replace(/^.*,(.+)\)/,'$1'));
if (rgbaValue < 0.9) {
square.style.backgroundColor = `rgba(0,0,0, ${rgbaValue + 0.1})`;
}
}))
}
function clearGrid() {
const squareReset = document.querySelectorAll(".squaregrid");
squareReset.forEach(square => square.style.backgroundColor = "");
squareReset.forEach(square => square.classList.remove("hover-change"));
}
css
.squaregrid {
box-sizing: border-box;
display: inline-block;
border: 0.05px solid black;
margin:0;
}
html, body {
line-height: 0;
height: 94vh;
width: 94vh;
margin-left: auto;
margin-right: auto;
}
#container {
display: block;
overflow: hidden;
margin-left: auto;
margin-right: auto;
border: 0.05px solid black;
}
.hover-change {
background-color: rgba(0, 0, 0, 1);
}
button {
display: block;
margin-bottom: 5px;
}
The main problem with your code has to do with the way the browser
handles full opacity.
The rgba() notation accepts a number in the range 0.0 to 1.0, inclusive, or a percentage, where the number 1 corresponds to 100%, for the value of the alpha channel. Any value outside the interval, though valid, is clamped to the nearest limit in the range.
A value greater than 1 or 100% will be clamped and
omitted from the computed value
background-color: rgba(0, 0, 0, 0.5); # computed (0, 0, 0, 0.5)
background-color: rgba(0, 0, 0, 1); # computed (0, 0, 0)
background-color: rgba(0, 0, 0, 1.3); # computed (0, 0, 0)
Your regex is capturing any character between the last comma
in the rgba() notation and the closing parenthesis of the computed
value. When the event listener is triggered after you have assigned
the square an alpha value equal or greater than 1 your regex captures 0
and sets the alpha value accordingly.
In the Code snippet below you can see one way of solving the problem using an other regular expression.
Alternatively you could make use of the opacity property as #Luca Neri
already suggested.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="author" content="">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body, html {
margin: 0;
}
.squaregrid {
display: grid;
}
.square {
background-color: rgba(20, 20, 20, 0.1);
}
button {
position: fixed;
bottom: 0;
}
</style>
</head>
<body>
<div class="squaregrid"></div>
<button onclick="clearScreen()">CLEAR</button>
<script>
//request number of rows
let rows = window.prompt("ENTER NUMBER OF ROWS")
//create a grid
document.getElementsByClassName("squaregrid")[0].style.gridTemplateColumns = "repeat("+ 1000/rows+", 1fr)"
//create Squares
for (let i = 0; i < 1000; i++) {
let newSquare = document.createElement("div")
newSquare.className = "square"
newSquare.style.height = 100/rows + "vh"
document.querySelector(".squaregrid").appendChild(newSquare)
}
//get HTML Collection of all squares
let allSquares = document.getElementsByClassName("square");
//loop HTML Collection and add a mouseover event listener to each square
for (square of allSquares) {
square.addEventListener("mouseover", event => {
//get computed background-color value of the square being hovered over
let squareBackground = getComputedStyle(event.target).backgroundColor;
//the regular expression
let regex = /\d\.\d/
//check if does not already have full opacity
if (squareBackground.match(regex) != null) {
//get the alpha channel of the square being hovered over
let squareOpacity = Number(squareBackground.match(regex))
//increase alpha value
event.target.style.backgroundColor = squareBackground.replace(regex, squareOpacity + 0.1)
}
})}
//reset every square on the grid
function clearScreen() {
for (square of allSquares) {
square.style.backgroundColor = "rgba(20, 20, 20, 0.1)"
}
}
</script>
</body>
</html>
Thanks for the feedback, #disinfor.
I try to create a running infotext on an infoscreen. The Text starts running from the right corner of the screen and leaves on the left side. This process repeats without a limit recursively.
Everything seems to work great, but when I remove the alert after debugging, the text don't start running from the right corner but from the left. Also the programm runs only one time.
HTML:
function setStaticData() {
sessionStorage.setItem('headerWidth', document.getElementById('header').scrollWidth);
}
function getStaticData() {
return sessionStorage.getItem('headerWidth');
}
function animation() {
//get element, its width & time param
var header = document.getElementById('header');
var headerWidth = getStaticData();
var headerTime = header.innerHTML.length * 0.3;
var windowWidth = window.innerWidth;
//clean all
header.style.transition = 'none';
header.style.marginLeft = windowWidth + 'px';
alert("baba"); //BAD BOY
//animate text
header.style.transition = 'margin linear ' + headerTime + 's';
header.style.marginLeft = '-' + headerWidth + 'px';
//wait and repeat
var delay = headerTime * 1000 + 1000;
setTimeout(animation, delay);
}
//first call
window.onload = function() {
setStaticData();
animation();
};
html {
width: 100%;
overflow: hidden;
}
body {
height: 100%;
width: 100%;
position: relative;
display: block;
margin: 0;
padding: 0;
top: 50vh;
transform: translateY(-50%);
color: black;
background-color: #bbc8d9;
}
header {
font-family: calibri, arial;
font-weight: 400;
font-size: 100px;
white-space: nowrap;
}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header id="header">+++ News, More News, Another Thing & something else +++</header>
<script src="functions.js"></script>
</body>
</html>
If I remove the bad boy [alert("baba");] it doesn't work, like I mentioned above.
I going crazy! Can you pls help me?
The problem is that changes to the style of an element are not processed until the Javascript returns to the main event loop and the page is rendered. If you make two assignments to a style, the browser only sees the final result. So when you set marginLeft to the window width and then set it to "-" + headerWidth + "px", only the second change is processed, so that's where the animation starts from.
The alert() causes the page to be rendered while it's waiting for your response (although I think this may be browser-dependent), which is why it works with that.
A simple solution is to put the second assignment in a setTimeout(), so it will be executed asynchronously after returning.
function setStaticData() {
//sessionStorage.setItem('headerWidth', document.getElementById('header').scrollWidth);
}
function getStaticData() {
return document.getElementById('header').scrollWidth; //sessionStorage.getItem('headerWidth');
}
function animation() {
//get element, its width & time param
var header = document.getElementById('header');
var headerWidth = getStaticData();
var headerTime = header.innerHTML.length * 0.3;
var windowWidth = window.innerWidth;
//clean all
header.style.transition = 'none';
header.style.marginLeft = windowWidth + 'px';
//alert("baba"); //BAD BOY
//animate text
setTimeout(function() {
header.style.transition = 'margin linear ' + headerTime + 's';
header.style.marginLeft = '-' + headerWidth + 'px';
}, 0);
//wait and repeat
var delay = headerTime * 1000 + 1000;
setTimeout(animation, delay);
}
//first call
window.onload = function() {
setStaticData();
animation();
};
html {
width: 100%;
overflow: hidden;
}
body {
height: 100%;
width: 100%;
position: relative;
display: block;
margin: 0;
padding: 0;
top: 50vh;
transform: translateY(-50%);
color: black;
background-color: #bbc8d9;
}
header {
font-family: calibri, arial;
font-weight: 400;
font-size: 100px;
white-space: nowrap;
}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header id="header">+++ News, More News, Another Thing & something else +++</header>
<script src="functions.js"></script>
</body>
</html>
Ok I think i fixed it. Its the timing problem just delay the animate call. 1s
function animation() {
//get element, its width & time param
var header = document.getElementById('header');
var headerWidth = getStaticData();
var headerTime = header.innerHTML.length * 0.3;
var windowWidth = window.innerWidth;
//clean all
header.style.transition = 'none';
header.style.marginLeft = windowWidth+'px';
// delay it for 1000ms
setTimeout(
function(){
//animate text
header.style.transition = 'margin linear '+headerTime+'s';
header.style.marginLeft = '-'+headerWidth+'px';
//wait and repeat
var delay = headerTime * 1000 + 1000;
setTimeout(animation, delay);
}, 1000);
}
Please look at the snippet below. I don't use sessionStorage as it is designed for a different purpose. I don't use setTimeout to wait for the styles to be applied.
I start the animation then restart it on transitionend event.
window.onload = function() {
var header = document.getElementById('header');
animation();
header.addEventListener('transitionend', function() {
header.style.transition = 'none';
header.style.transform = 'translateX(0)';
animation();
});
function animation() {
var t = header.offsetWidth / 70,
tx = header.scrollWidth;
header.style.transition = 'transform ' + t + 's linear';
header.style.transform = 'translateX(-' + tx + 'px)';
}
};
html {
width: 100%;
overflow: hidden;
background-color: #bbc8d9;
}
header {
font: 100px sans-serif;
white-space: nowrap;
padding-left: 100%;
}
<header id="header">+++ News, More News, Another Thing & something else +++</header>
I'm a graphic designer in Portugal, used to work with code everyday, like css, html and a bit javascript and php. I am currently developing an interactive logo button, but it has to be PNG to look the way I want. This is the javascript code on html (image is hosted in my website):
I want to create a mouseclick start and stop on last/first frame, not a infinite loop like this, and reversed animation after click to open/close. Basically, the lock and unlock of the padlock.
The point of this animation is to open a menu nav-bar under the logo. Can you help me?
My code:
var cSpeed = 5;
var cWidth = 200;
var cHeight = 145;
var cTotalFrames = 7;
var cFrameWidth = 200;
var cImageSrc = 'https://www.studiogo.net/sprites.png';
var cImageTimeout = false;
var cIndex = 0;
var cXpos = 0;
var SECONDS_BETWEEN_FRAMES = 0;
function startAnimation() {
document.getElementById('loaderImage').style.backgroundImage = 'url(' + cImageSrc + ')';
document.getElementById('loaderImage').style.width = cWidth + 'px';
document.getElementById('loaderImage').style.height = cHeight + 'px';
//FPS = Math.round(100/(maxSpeed+2-speed));
FPS = Math.round(100 / cSpeed);
SECONDS_BETWEEN_FRAMES = 1 / FPS;
setTimeout('continueAnimation()', SECONDS_BETWEEN_FRAMES / 1000);
}
function continueAnimation() {
cXpos += cFrameWidth;
//increase the index so we know which frame of our animation we are currently on
cIndex += 1;
//if our cIndex is higher than our total number of frames, we're at the end and should restart
if (cIndex >= cTotalFrames) {
cXpos = 0;
cIndex = 0;
}
document.getElementById('loaderImage').style.backgroundPosition = (-cXpos) + 'px 0';
setTimeout('continueAnimation()', SECONDS_BETWEEN_FRAMES * 1000);
}
function imageLoader(s, fun) //Pre-loads the sprites image
{
clearTimeout(cImageTimeout);
cImageTimeout = 0;
genImage = new Image();
genImage.onload = function() {
cImageTimeout = setTimeout(fun, 0)
};
genImage.onerror = new Function('alert(\'Could not load the image\')');
genImage.src = s;
}
//The following code starts the animation
new imageLoader(cImageSrc, 'startAnimation()');
<div id="loaderImage"></div>
Please, see if this is what you want.
$(document).ready(function () {
$(".lock").click(function () {
var $self = $(this);
if ($self.hasClass("closed")) {
$self.removeClass("close");
setTimeout(function () {
$self.addClass("open").removeClass("closed");
}, 100);
} else {
$self.removeClass("open");
setTimeout(function () {
$self.addClass("close").addClass("closed");
}, 100);
}
});
});
div.lock {
background-image: url('https://www.studiogo.net/sprites.png');
width: 200px;
height: 145px;
background-position: 0 center;
background-repeat: no-repeat;
}
div.closed {
background-position: -1200px center;
}
div.close {
animation: close-animation 300ms steps(6, start); // 1200px / 200px = 6
}
div.open {
animation: close-animation 300ms steps(6, end); // 1200px / 200px = 6
animation-fill-mode: backwards;
animation-direction: reverse;
}
#keyframes close-animation {
from {
background-position: 0 center;
}
to {
background-position: -1200px center;
}
}
<div class="lock closed">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm using the PDF.js library to render a pdf into the canvas. That pdf has hyperlinks in there, The PDF.js library is drawing the pdf into the canvas but the hyperlinks don't work.
Any Idea if it possible that hyperlinks work into the canvas?
Thanks
Here is a fiddle that shows you how to enable annotations (including hyperlinks) in PDF files.
The original PDF file used in the fiddle is here.
I used viewer code (web/page_view.js,web/viewer.css) as refrence to write this fiddle.
HTML:
<!doctype html>
<html lang="en">
<head>
<link href="style.css" rel="stylesheet" media="screen" />
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="http://seikichi.github.io/tmp/PDFJS.0.8.715/pdf.min.js" type="text/javascript"></script>
<script src="http://seikichi.github.io/tmp/PDFJS.0.8.715/ui_utils.js"></script>
<script src="./main.js" type="text/javascript"></script>
</head>
<body>
<div id="pdfContainer" class="pdf-content">
<canvas id="the-canvas"></canvas>
<div class="annotationLayer"></div>
</div>
</body>
</html>
CSS:
body {
font-family: arial, verdana, sans-serif;
}
.pdf-content {
border: 1px solid #000000;
}
.annotationLayer > a {
display: block;
position: absolute;
}
.annotationLayer > a:hover {
opacity: 0.2;
background: #ff0;
box-shadow: 0px 2px 10px #ff0;
}
.annotText > div {
z-index: 200;
position: absolute;
padding: 0.6em;
max-width: 20em;
background-color: #FFFF99;
box-shadow: 0px 2px 10px #333;
border-radius: 7px;
}
.annotText > img {
position: absolute;
opacity: 0.6;
}
.annotText > img:hover {
opacity: 1;
}
.annotText > div > h1 {
font-size: 1.2em;
border-bottom: 1px solid #000000;
margin: 0px;
}
JavaScript:
PDFJS.workerSrc = 'http://seikichi.github.io/tmp/PDFJS.0.8.715/pdf.min.worker.js';
$(function () {
var pdfData = loadPDFData();
PDFJS.getDocument(pdfData).then(function (pdf) {
return pdf.getPage(1);
}).then(function (page) {
var scale = 1;
var viewport = page.getViewport(scale);
var $canvas = $('#the-canvas');
var canvas = $canvas.get(0);
var context = canvas.getContext("2d");
canvas.height = viewport.height;
canvas.width = viewport.width;
var $pdfContainer = $("#pdfContainer");
$pdfContainer.css("height", canvas.height + "px")
.css("width", canvas.width + "px");
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
setupAnnotations(page, viewport, canvas, $('.annotationLayer'));
});
function setupAnnotations(page, viewport, canvas, $annotationLayerDiv) {
var canvasOffset = $(canvas).offset();
var promise = page.getAnnotations().then(function (annotationsData) {
viewport = viewport.clone({
dontFlip: true
});
for (var i = 0; i < annotationsData.length; i++) {
var data = annotationsData[i];
var annotation = PDFJS.Annotation.fromData(data);
if (!annotation || !annotation.hasHtml()) {
continue;
}
var element = annotation.getHtmlElement(page.commonObjs);
data = annotation.getData();
var rect = data.rect;
var view = page.view;
rect = PDFJS.Util.normalizeRect([
rect[0],
view[3] - rect[1] + view[1],
rect[2],
view[3] - rect[3] + view[1]]);
element.style.left = (canvasOffset.left + rect[0]) + 'px';
element.style.top = (canvasOffset.top + rect[1]) + 'px';
element.style.position = 'absolute';
var transform = viewport.transform;
var transformStr = 'matrix(' + transform.join(',') + ')';
CustomStyle.setProp('transform', element, transformStr);
var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
CustomStyle.setProp('transformOrigin', element, transformOriginStr);
if (data.subtype === 'Link' && !data.url) {
// In this example, I do not handle the `Link` annotations without url.
// If you want to handle those annotations, see `web/page_view.js`.
continue;
}
$annotationLayerDiv.append(element);
}
});
return promise;
}
});
function loadPDFData() {
/*jshint multistr: true */
var base64pdfData = '...'; //should contain base64 representing the PDF
function base64ToUint8Array(base64) {
var raw = atob(base64);
var uint8Array = new Uint8Array(new ArrayBuffer(raw.length));
for (var i = 0, len = raw.length; i < len; ++i) {
uint8Array[i] = raw.charCodeAt(i);
}
return uint8Array;
}
return base64ToUint8Array(base64pdfData);
}
Enable Text Selection in PDF.JS
Step 1: Adding a Element to Hold the Text Layer
<div id="text-layer"></div>
This div will be in addition to the element where the PDF is rendered, so the HTML will look like :
<canvas id="pdf-canvas"></canvas>
<div id="text-layer"></div>
Step 2 : Adding CSS for Text Layer
Add the following to your CSS file :
#text-layer {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
opacity: 0.2;
line-height: 1.0;
}
#text-layer > div {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
}
Step 3: Getting the PDF Text
After the PDF has been rendered in the canvas, you need to get the text contents of the PDF, and place that text in the text layer.
// page is the page context of the PDF page
// viewport is the viewport required in renderContext
// For more see https://usefulangle.com/post/20/pdfjs-tutorial-1-preview-pdf-during-upload-wih-next-prev-buttons
page.render(renderContext).then(function() {
// Returns a promise, on resolving it will return text contents of the page
return page.getTextContent();
}).then(function(textContent) {
// PDF canvas
var pdf_canvas = $("#pdf-canvas");
// Canvas offset
var canvas_offset = pdf_canvas.offset();
// Canvas height
var canvas_height = pdf_canvas.get(0).height;
// Canvas width
var canvas_width = pdf_canvas.get(0).width;
// Assign CSS to the text-layer element
$("#text-layer").css({ left: canvas_offset.left + 'px', top: canvas_offset.top + 'px', height: canvas_height + 'px', width: canvas_width + 'px' });
// Pass the data to the method for rendering of text over the pdf canvas.
PDFJS.renderTextLayer({
textContent: textContent,
container: $("#text-layer").get(0),
viewport: viewport,
textDivs: []
});
});
source: https://usefulangle.com/post/90/javascript-pdfjs-enable-text-layer
setupAnnotations = (page, viewport, canvas, annotationLayerDiv) => {
let pdfjsLib = window['pdfjs-dist/build/pdf'];
let pdfjsViewer = window['pdfjs-dist/web/pdf_viewer'];
//BELOW--------- Create Link Service using pdf viewer
let pdfLinkService = new pdfjsViewer.PDFLinkService();
page.getAnnotations().then(function (annotationsData) {
viewport = viewport.clone({
dontFlip: true
});
let pdf_canvas = canvas;
// Render the annotation layer
annotationLayerDiv.style.left = pdf_canvas.offsetLeft + 'px';
annotationLayerDiv.style.top = pdf_canvas.offsetTop + 'px';
annotationLayerDiv.style.height = viewport.height + 'px';
annotationLayerDiv.style.width = viewport.width + 'px';
pdfjsLib.AnnotationLayer.render({
viewport: viewport,
div: annotationLayerDiv,
annotations: annotationsData,
page: page,
linkService: pdfLinkService,
enableScripting: true,
renderInteractiveForms: true
});
}
IMP ---- Do not forget to add this CSS
.annotation-layer{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
opacity: 1;
}
.annotation-layer section{
position: absolute;
cursor: pointer;
}
.annotation-layer section a{
display: block;
width: 100%;
height: 10px;
}
In above example, Link service instance is created using class in pdf viewer, which is being passed as parameter to annotation layer render method.
Please refer source code of PDF.js refer to /web/pdf_viewer.js - class PDFLinkService for more information.
PDF.js version - v2.9.359