I am preying somebody can help me out..
Brief History:
Im building a radio station on the internet, the Webpage will display metadata for songs playing and songs played in the past...
So far i have managed to build a MYSQL database, Query the database in PHP, encode the array of data in JSON, use ajax to set a timer to query the database and JavaScript to parse the data to HTML...
I have also managed to get my head around HTML & CSS to an extent..
I am brand new to coding and everything is coming along nicely and am doing some courses in JavaScript but what i want is far too advanced for any course i have taken so far....
The Question:
I wish to make a TEXT marquee that will display the name of the song currently playing.
The text marquee will have some rules.
Rule 1: The name of the song will stay static on screen if it is equal to or less than the display area.
Rule 2: If the name of the song is greater than the display area then it needs to animate.
Before you go any further i need to note that i have looked at a few JQuery marquees but none of them suite my needs
The Maths:
FIXED DIV WIDTH = 257.4 Pixels
(This is the "wrapper" which will contain the variable text (which is a little bigger than my MAXIMUM TEXT WIDTH so it looks pretty...)
VARIABLE TEXT WIDTH = x characters
(defined by "list0title" which is variable data from MYSQL dB) (the characters will need to be converted to pixels for use with MAXIMUM TEXT WIDTH)
MAXIMUM TEXT WIDTH = 250 Pixels
(this is the maximum text that fits in the "wrapper" comfortably, if VARIABLE TEXT WIDTH is equal to or less than MAXIMUM TEXT WIDTH the text will be static, otherwise the text will animate in a ping pong style)
ANIMATION:
VARIABLE TEXT WIDTH - MAXIMUM TEXT WIDTH = PIXELS TO ANIMATE
(for example if my VARIABLE TEXT WIDTH was 150 pixels and my MAXIMUM TEXT WIDTH was 100 PIXELS i would need to animate the text by 50 pixels 150-100 = 50 pixels)
So this above is me explaining The JavaScript / CSS intergrated script i need in the best way i can..
I will include my JavaScript Below so you can see how the information is obtained from the MYSQL database and also provide the example code for the animation.
JAVASCRIPT:
function getPlaylist()
{
var xmlhttp,
timer;
if (window.XMLHttpRequest)
{
xmlhttp = new XMLHttpRequest();
}
else
{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function ()
{
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
var list = eval ('('+xmlhttp.responseText+')');
{
document.getElementById("list0artist").innerHTML=list[0].artist;
document.getElementById("list0title").innerHTML=list[0].title;
document.getElementById("list0titlelength").innerHTML=list[0].title.clientwidth;
document.getElementById("list0label").innerHTML=list[0].label;
document.getElementById("list0albumyear").innerHTML=list[0].albumyear;
document.getElementById("list0picture").innerHTML='<img src="/testsite/covers/' + list[0].picture + '" width="169" height="169"/>';
document.getElementById("list1artist").innerHTML=list[1].artist;
document.getElementById("list1title").innerHTML=list[1].title;
document.getElementById("list1label").innerHTML=list[1].label;
document.getElementById("list1albumyear").innerHTML=list[1].albumyear;
document.getElementById("list1picture").innerHTML='<img src="/testsite/covers/' + list[1].picture + '" width="83px" height="83px"/>';
document.getElementById("list2artist").innerHTML=list[2].artist;
document.getElementById("list2title").innerHTML=list[2].title;
document.getElementById("list2label").innerHTML=list[2].label;
document.getElementById("list2albumyear").innerHTML=list[2].albumyear;
document.getElementById("list2picture").innerHTML='<img src="/testsite/covers/' + list[2].picture + '" width="83px" height="83px"/>';
document.getElementById("list3artist").innerHTML=list[3].artist;
document.getElementById("list3title").innerHTML=list[3].title;
document.getElementById("list3label").innerHTML=list[3].label;
document.getElementById("list3albumyear").innerHTML=list[3].albumyear;
document.getElementById("list3picture").innerHTML='<img src="/testsite/covers/' + list[3].picture + '" width="83px" height="83px"/>';
document.getElementById("list4artist").innerHTML=list[4].artist;
document.getElementById("list4title").innerHTML=list[4].title;
document.getElementById("list4label").innerHTML=list[4].label;
document.getElementById("list4albumyear").innerHTML=list[4].albumyear;
document.getElementById("list4picture").innerHTML='<img src="/testsite/covers/' + list[4].picture + '" width="83px" height="83px"/>';
}
}
};
xmlhttp.onerror = function()
{
clearTimeout(timer);
};
xmlhttp.open("GET", "playlist.php?t=" + Math.random(), true);
xmlhttp.send();
timer = setTimeout(getPlaylist, 1000);
}
CSS ANIMATION:
<!DOCTYPE html>
<html>
<head>
<script src="/testsite/OneSecondPlaylist.js"></script>
<style type="text/css">
#wrapper {
width: 257.4px;
height: 41.85px;
overflow: hidden;
background-color: #383838;
color: #FFFFFF;
line-height: 41.85px;
font-size: 80%;
font-family: baumans;
color: #FFFFFF;
}
.textbox {
-moz-animation-duration: 4s;
-moz-animation-name: slidein;
-moz-animation-iteration-count: infinite;
-moz-animation-direction: alternate;
-moz-animation-timing-function: linear;
white-space:nowrap;
}
#-moz-keyframes slidein {
from {
margin-left:3%;
}
to {
margin-left:-33%;
}
</style>
</head>
<body onload="getPlaylist()">
<script>var __adobewebfontsappname__="dreamweaver"</script><script src="http://use.edgefonts.net/baumans:n4:default.js" type="text/javascript"></script>
<div id="wrapper">
<div id="list0artist"></div>
</div>
<div id="wrapper">
<div id="list0title" class="textbox"></div>
</div>
</body>
</html>
ANIMATION NOTE: I am aware that i need to play with the webkits to get it working on all browsers
I hope i have not included too much code, i felt it was important to show it in its entirety as you may have some pointers outside of the question i am asking..
So to sum up i am basically asking you guys to help me code a JavaScript / CSS intergrated script that will animate based upon the rules i have defined.
Thanks for reading this through, i hope you dont think im being lazy asking for this, i feel i am making good headway for somebody who has zero coding knowledge and just need a push in the right direction!!!.. Many Thanks!!! Justin..
var texts = document.getElementsByClassName('text'); // Class I added to innards
for (var i = 0; i < texts.length; i++) {
if (texts[i].parentNode.offsetWidth < texts[i].offsetWidth) {
texts[i].classList.add("textbox");
}
}
Demo
Some notes:
You can't have decimal pixels like .4px, it will be rounded up or down based on the browser
I'd recommend pausing at some point in the animation, continuous movement is annoying for users
Using margins is kind of iffy, I might opt for using left instead. This is what I did in the demo
Edit
To fix the positioning issue for the animation you can use transforms instead of position:absolute.
transform:translateX(calc(-100% + 257px)); /* -100% + parent width */
Demo
But like I said before, using a js solution would likely be better
Related
I'm trying to create a marquee (yes, I've done LOTS of searching on that topic first) using animated text-indent. I prefer this solution over others I've tried, like using translation 100%, which causes text to leak out beyond the boundaries of my marquee.
I've been trying to follow this example here: https://www.jonathan-petitcolas.com/2013/05/06/simulate-marquee-tag-in-css-and-javascript.html
...which I've updated a bit, doing it in TypeScript, using API updates (appendRule instead of insertRule) and dropping concerns about old browser support.
The problem is that the animation restarts using the old keyframe rules -- the step described by the comment "re-assign the animation (to make it run)" doesn't work.
I've looked at what's going on in a debugger, and the rules are definitely being changed -- old rules deleted, new rules added. But it's as if the old rules are cached somewhere, and they aren't being cleared out.
Here's my current CSS:
#marquee {
position: fixed;
left: 0;
right: 170px;
bottom: 0;
background-color: midnightblue;
font-size: 14px;
padding: 2px 1em;
overflow: hidden;
white-space: nowrap;
animation: none;
}
#marquee:hover {
animation-play-state: paused;
}
#keyframes marquee-0 {
0% {
text-indent: 450px;
}
100% {
text-indent: -500px;
}
}
And the relevant section of my TypeScript:
function updateMarqueeAnimation() {
const marqueeRule = getKeyframesRule('marquee-0');
if (!marqueeRule)
return;
marquee.css('animation', 'unset');
const element = marquee[0];
const textWidth = getTextWidth(marquee.text(), element);
const padding = Number(window.getComputedStyle(element).getPropertyValue('padding-left').replace('px', '')) +
Number(window.getComputedStyle(element).getPropertyValue('padding-right').replace('px', ''));
const offsetWidth = element.offsetWidth;
if (textWidth + padding <= offsetWidth)
return;
marqueeRule.deleteRule('0%');
marqueeRule.deleteRule('100%');
marqueeRule.appendRule('0% { text-indent: ' + offsetWidth + 'px; }');
marqueeRule.appendRule('100% { text-indent: -' + textWidth + 'px; }');
setTimeout(() => marquee.css('animation', 'marquee-0 15s linear infinite'));
}
I've tried a number of tricks so far to get around this problem, including things like cloning the marquee element and replacing it with its own clone, and none of that has helped -- the animation continues to run as if the original stylesheet values are in effect, so the scrolling of the marquee doesn't adapt to different widths of text.
The next thing I'll probably try is dynamically creating new keyframes objects instead of editing the rules inside of an existing keyframes object, but that's a messy solution I'd rather avoid if anyone has a better solution.
I found a way to get my marquee working, and it did involved dynamically adding and removing keyframes rules from a stylesheet, but that wasn't as painful or ugly as I thought it might be.
let animationStyleSheet: CSSStyleSheet;
let keyframesIndex = 0;
let lastMarqueeText = '';
function updateMarqueeAnimation(event?: Event) {
const newText = marquee.text();
if (event === null && lastMarqueeText === newText)
return;
lastMarqueeText = newText;
marquee.css('animation', 'none');
const element = marquee[0];
const textWidth = getTextWidth(newText, element);
const padding = Number(window.getComputedStyle(element).getPropertyValue('padding-left').replace('px', '')) +
Number(window.getComputedStyle(element).getPropertyValue('padding-right').replace('px', ''));
const offsetWidth = element.offsetWidth;
if (textWidth + padding <= offsetWidth)
return;
if (!animationStyleSheet) {
$('head').append('<style id="marquee-animations" type="text/css"></style>');
animationStyleSheet = ($('#marquee-animations').get(0) as HTMLStyleElement).sheet as CSSStyleSheet;
}
if (animationStyleSheet.cssRules.length > 0)
animationStyleSheet.deleteRule(0);
const keyframesName = 'marquee-' + keyframesIndex++;
const keyframesRule = `#keyframes ${keyframesName} { 0% { text-indent: ${offsetWidth}px } 100% { text-indent: -${textWidth}px; } }`;
const seconds = (textWidth + offsetWidth) / 100;
animationStyleSheet.insertRule(keyframesRule, 0);
marquee.css('animation', `${keyframesName} ${seconds}s linear infinite`);
}
There's other stuff going on here not needed for a general solution. One thing is that this method is called for two reasons: The window is being resized, or an update to the marquee text has been made. I always want to update when the window is resized, but otherwise I don't want to update the animation if the text hasn't changed, otherwise it could unnecessarily reset when someone is trying to read it.
The other thing is that I don't want text to scroll at all if it happens to fit nicely without scrolling.
I’m having an issue with my script that loads content to a page via jQuery .load(). The content loads properly, my animation of said content works (done with class "hidden"), but I’m setting the height of the wrapping container before loading, and then animating the height of said container to keep the page from jumping between the different content heights.
Essentially what’s happening, is the height is getting set to 0 for some reason, rather than the actual height of the element. Strangely, this seems to work on the initial click, but then on any others it breaks and sets the height to 0.
See the code below (I would create a jsFiddle but, .load() doesn't work with that):
HTML
<main id="content" class="content">
<div id="content-inner" class="content-inner">
<!-- Content -->
</div>
</main>
CSS
.content {
transition: .25s height;
}
.content-inner {
position: relative;
top: 0;
opacity: 1;
visibility: visible;
transition: .25s opacity, .25s top;
}
.hidden .content-inner {
top: -30px;
opacity: 0;
visibility: hidden;
transition: .25s opacity, .25s top, 0s visibility .25s;
}
JavaScript (jQuery)
var $mainContentOuter = $('#content'),
linkContent = '#content-inner',
$mainContentInner = $(linkContent);
function loadMainContent(link) {
// Assign height as current height to prevent jumping
$mainContentOuter.height( $mainContentInner.outerHeight() );
// Hide content
$mainContentOuter.addClass('hidden').delay(250).queue(function() {
// Load content
$mainContentOuter.load(link + ' ' + linkContent, function() {
// Animate the height difference when loaded
$mainContentOuter.height($mainContentInner.outerHeight());
});
// Dequeue for delay
$(this).dequeue();
}).delay(250).queue(function() {
// Reveal content and reset height
$mainContentOuter.removeClass('hidden').css('height','');
// Dequeue for delay
$(this).dequeue();
});
}
// Override behavior of navigational links
$('.nav-main > li > a').click(function(e){
var link = $(this).attr('href');
//Pass link
loadMainContent(link);
e.preventDefault();
});
Any help would be greatly appreciated.
Thanks in advance,
Rob
The problem is that you load the inner-content inside the outer content, so there is no inner-content after the load takes place. Try using:
$mainContentOuter.load(link + ' ' + '#content', function() {
// Animate the height difference when loaded
$mainContentOuter.height($mainContentInner.outerHeight());
});
Imagine it like you have 2 rectangles A and B, where B is inside A. If you load everything that B has and give it to A then there will be no B, but only A because B didn't have B in it, so A won't have B in it either. I recently encoutered a similar problem and it took me hours to understand and solve it. Please let me know if this solved your problem!
Thanks to #thanasis I realized what was going on here.
The variable $mainContentInner was storing a reference to the original object in the DOM. Once the page content was loaded, this object was removed and replaced with another one, albeit similar.
Even though they have the same ID, they're different objects. To get around this, I redefined the variable, so as to target the new object. See below:
// Load content
$mainContentOuter.load(link + ' ' + linkContent, function() {
// Redefine $mainContentInner to the new object
$mainContentInner = $(linkContent);
// Animate the height difference when loaded
$mainContentOuter.height($mainContentInner.outerHeight());
});
I'm working on an HTML5 browser game that can be divided into 3 parts: two UI panels on the left and right of a center set of square canvases for the playing surface. The three panels need to be horizontally aligned, and the total game needs to keep an aspect ratio of 16:9. The left and right panels should be of equal widths, and all three panels must be of equal height. I have specified a minimum width and height inside a resize() function called when an onresize event is detected.
Currently, each panel is a div, and all three are contained inside a section. Right now, the section isn't necessary, but I want to keep the game separated from extra content at the bottom of the screen that I might choose to add later.
The CSS style is as follows:
* {
vertical-align: baseline;
font-weight: inherit;
font-family: inherit;
font-style: inherit;
font-size: 100%;
border: 0 none;
outline: 0;
padding: 0;
margin: 0;
}
#gameSection {
white-space: nowrap;
overflow-x: hide;
overflow-y: hide;
}
#leftPanel, #centerPanel, #rightPanel {
display: inline-block;
}
#leftPanel {
background-color: #6495ed;
}
#centerPanel {
background-color: #e0ffff;
}
#rightPanel {
background-color: #b0c4de;
Right now, I have set the background color of each div just to show me when I'm correctly setting the size of each div.
The body of my HTML document is as follows:
<body onresize="resize()">
<section id="gameSection">
<div id="leftPanel">Left Panel.</div>
<div id="centerPanel">Center Panel.</div>
<div id="rightPanel">Right Panel.</div>
</section>
</body>
And finally, my resize() function (I created a separate function for resizing the game in case I add more elements below later):
function resize() {
var MIN_GAME_WIDTH = 800;
var MIN_GAME_HEIGHT = 450;
var GAME_ASPECT_RATIO = 16 / 9;
var width = window.innerWidth;
var height = window.innerHeight;
var gWidth, gHeight;
if(width < MIN_GAME_WIDTH || height < MIN_GAME_HEIGHT) {
gWidth = MIN_GAME_WIDTH;
gHeight = MIN_GAME_HEIGHT;
}
else if ((width / height) > GAME_ASPECT_RATIO) {
<!-- width is too large for height -->
gHeight = height;
gWidth = height * GAME_ASPECT_RATIO;
}
else {
<!-- height is too large for width -->
gWidth = width;
gHeight = width / GAME_ASPECT_RATIO;
}
resizeGame(gWidth, gHeight, GAME_ASPECT_RATIO);
}
function resizeGame(var gWidth, var gHeight, var aspectRatio) {
var gSection = document.getElementById("gameSection");
var lPanel = document.getElementById("leftPanel");
var cPanel = document.getElementById("centerPanel");
var rPanel = document.getElementById("rightPanel");
gSection.height = gHeight;
gSection.width = gWidth;
<!-- should the below be taken care of in the CSS? -->
lPanel.height = gHeight;
cPanel.height = gHeight;
rPanel.height = gHeight;
cPanel.width = cPanel.height;
lPanel.width = (gWidth - cPanel.width) / 2;
rPanel.width = lPanel.width;
}
I've tried a number of different commands to resize the divs, but it just isn't working for me. When I try adding test canvases, color appears, but the boxes still aren't the correct size. I have also considered loading an invisible background image to each div and scaling it to the desired size; however, I was able to resize my canvas using the above method before and it seemed to work just fine.
Additional Notes
While I've already had pretty good success resizing a single canvas, I don't want to use just one canvas for the game because not all parts of the UI need to be drawn at the same time.
I'm trying to keep this solely in Javascript.
I suspect that I could just use CSS to handle resizing by fixing the aspect ratio to 16:9 and using width:56.25% for the center panel and width:21.875% for the side panels, but that limits me to one aspect ratio and doesn't explain why my above script isn't working.
I can provide the entire HTML file if needed. This is what it's supposed to look like:
End Goal (without right panel)
Thank you!
UDPATE:
jsfiddle
I got it kind of working here. I made a lot of changes/minor fixes to the code before finding what was wrong (other than various syntax errors):
You were using .width and .height instead of .style.width and .style.height, and you were applying integers to these instead of strings with "px" appended to them. Both of these things are completely understandable to miss.
I also moved the onresize from the body tag into the JS, don't know why it wasn't working on jsfiddle, but this is good practice anyways.
In the future: learn how to debug JS using the console and when you ask questions, use small examples, not your entire codebase. This question could have been simplified to "How do I resize a div?" with one line of JS and one div. You also should consider not doing this specific thing in JS, and using flexbox as redbmk said.
I need help with an animated PNG in Javascript.
I found how to animate a PNG background with Javascript here on Stack Overflow. But my problem is that I only need the animation onmouseover and onmouseout. And the animation should play only once, not in a loop, so when the user moves the mouse over a div the the animation in the background should play once and stop at the last frame, but when the user goes off the div, a reverse animation should play once and stop at the last (first) frame. The script I found here is:
The style:
#anim {
width: 240px; height: 60px;
background-image: url(animleft.png);
background-repeat: no-repeat;
}
The HTML:
<div id="anim"></div>
Javascript:
var scrollUp = (function () {
var timerId; // stored timer in case you want to use clearInterval later
return function (height, times, element) {
var i = 0; // a simple counter
timerId = setInterval(function () {
if (i > times) // if the last frame is reached, set counter to zero
i = 0;
element.style.backgroundPosition = "0px -" + i * height + 'px'; //scroll up
i++;
}, 100); // every 100 milliseconds
};
})();
// start animation:
scrollUp(14, 42, document.getElementById('anim'))
I hope anyone can help me, Thank you
To stop the animation after the first set of frames you do want to change the if condition to not reset the counter to zero but instead to clear the interval and stop it from reoccuring.
To only let it play when you enter an element you can attach the animation function as an event listener and play the whole thing in reverse with another function that you plug into your onmouseout event.
Depending on your target browser and since your question is fairly vague I can recommend two alternatives to you:
Use jQuery animate (all browsers, include ref to jquery)
//Animate to x,y where x and y are final positions
$('#anim').mouseenter(function(e) {
$(this).animate({background-position: x + 'px ' + y + 'px'}, 4200);
})
//Do same for mouseleave
Use a css3 animation (using -webkit browser here)
<style>
#-webkit-keyframes resize {
100% {
height: 123px;
}
}
#anim:hover {
-webkit-animation-name: resize;
-webkit-animation-duration: 4s;
}
I would choose option 2 if you are doing mobile development or can choose only css3 capable browsers.
I am having some issues finding a decent tutorial for generating a loading style screen with regards to HTML5. To be quite honest I'm not sure exactly where to begin...
My project is essentially a simple HTML5 game, where I'll be loading in various sprite sheets and tilesets. They'll be reasonably small, but I'd like to show a little loading spinner instead of a blank screen while all the resources are loaded.
Much appreciated if somebody could point me in the right direction, be it decent links or code samples to get me going...my Google-fu is lacking today!
For clarification, I need to figure out how to load the resources themselves, as opposed to finding a spinner. For instance, how to calculate that X% has loaded.
Edit 2
Silly me, I can just check for <variable>.image.complete. Voted to close.
This website is excellent for animated loading gifs: http://ajaxload.info/. An overlay can be used to display the image and also prevent interaction with the page while it is loading. You can use a div, position it above everything else (z-index) and set the width and height to 100%.
An old question, but it is usefull to know that the Image object also has an onload event. It is often way better to use that than check for complete in for example a loop.
Example usage (not actually checked if working):
var numImagesLoaded = 0;
function incrementAndCheckLoading(){
numImagesLoaded++;
if(numImagesLoaded == 2){
// Do some stuff like remove loading screen or redirect to other URL
}
}
image1 = new Image();
image1.src = "image1.jpg"
image1.onload = incrementAndCheckLoading;
image2 = new Image();
image2.src = "image2.jpg"
image2.onload = incrementAndCheckLoading;
This is an excellent resource for demonstrating HTML5 CSS animation.
http://slides.html5rocks.com/#css-animation
#-webkit-keyframes pulse {
from {
opacity: 0.0;
font-size: 100%;
}
to {
opacity: 1.0;
font-size: 200%;
}
}
div {
-webkit-animation-name: pulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-direction: alternate;
}
This demonstrates how to create a loading status bar:
http://slides.html5rocks.com/#semantic-tags-2
<progress>working...</progress>
There are plenty of other examples on those slides as well that may have what you're looking for.
You can use <progress> element in HTML5. See this page for source code and live demo.
http://purpledesign.in/blog/super-cool-loading-bar-html5/
here is the progress element...
<progress id="progressbar" value="20" max="100"></progress>
this will have the loading value starting from 20. Of course only the element wont suffice. You need to move it as the script loads. For that we need JQuery. Here is a simple JQuery script that starts the progress from 0 to 100 and does something in defined time slot.
<script>
$(document).ready(function() {
if(!Modernizr.meter){
alert('Sorry your brower does not support HTML5 progress bar');
} else {
var progressbar = $('#progressbar'),
max = progressbar.attr('max'),
time = (1000/max)*10,
value = progressbar.val();
var loading = function() {
value += 1;
addValue = progressbar.val(value);
$('.progress-value').html(value + '%');
if (value == max) {
clearInterval(animate);
//Do Something
}
if (value == 16) {
//Do something
}
if (value == 38) {
//Do something
}
if (value == 55) {
//Do something
}
if (value == 72) {
//Do something
}
if (value == 1) {
//Do something
}
if (value == 86) {
//Do something
}
};
var animate = setInterval(function() {
loading();
}, time);
};
});
</script>
Add this to your HTML file.
<div class="demo-wrapper html5-progress-bar">
<div class="progress-bar-wrapper">
<progress id="progressbar" value="0" max="100"></progress>
<span class="progress-value">0%</span>
</div>
</div>
Hope this will give you a start.
Because this question is tagged with html5 - I think we can do better than a spinner gif. A more modern alternative:
http://fgnass.github.io/spin.js/