I am using svg.js and would like to play a series of animations and then provide a slider for the user to control the animation to look at it more closely. It looks like when an animation is complete, svg.js cleans up the resources so the situation gets nulled out so I can't reset it. I have a fiddle with a simplified example. After the animation plays once, I would like to then be able to use the slider and use the at method to reposition the animations.
https://jsfiddle.net/voam/xeajbhea/2/
var draw = SVG('drawing'),
rectA1 = draw.rect(50, 50).fill('#f06'),
rectA2 = draw.rect(50, 50).fill('#60f').translate(200),
rectB1 = draw.rect(50, 50).fill('#ce0').translate(0, 100).opacity(0),
rectB2 = draw.rect(50, 50).fill('#0ec').translate(200, 100).opacity(0);
var animation1 = rectA1.animate().move(200).opacity(0);
var animation2 = rectA2.animate().move(-200).opacity(0).during(function(pos) {
document.getElementById("range_animation").value = pos;
});
var animation3, animation4;
animation2.after(function(situation) {
animation3 = rectB1.animate().move(200).opacity(1);
animation4 = rectB2.animate().opacity(1).move(-200)
.during(function(pos) {
document.getElementById("range_animation").value = 1 + pos;
});
console.log('animations', animation1, animation2, animation3, animation4);
});
document.getElementById("range_animation").addEventListener("change", function(el) {
///console.log('change', el, );
var val = this.value;
if (animation1.situation) {
if (val < 1) {
animation1.at(val).pause();
animation2.at(val).pause();
} else {
animation3.at(val - 1).pause();
animation4.at(val - 1).pause();
}
}
}, false);
HTML below:
<div id="drawing">
</div>
<input type="range" id="range_animation" min="0.01" max="2" step=".01" value=".01">
One approach that works is as #Fuzzyma mentioned is to make the animations loop so they don't get terminated. Then one has to manage the transition sets from one to another. Any animations that don't get run immediately start in a paused state. In my real project not this demo there are 5 animation sets each with subanimations so lets see if this turns out to be manageable!
var atEnd = function (pos) {
var rounded = pos.toFixed(2);
return (rounded == .99 || rounded == 1.00);
};
var draw = SVG('drawing')
, rectA1 = draw.rect(50, 50).fill('#f06')
, rectA2= draw.rect(50, 50).fill('#60f').translate(200)
, rectB1 = draw.rect(50, 50).fill('#ce0').translate(0, 100).opacity(0)
, rectB2 = draw.rect(50, 50).fill('#0ec').translate(200, 100).opacity(0);
var animation3, animation4, lastTotalRange;
var animation1 = rectA1.animate().move(200).opacity(0).loop();
var animation2 = rectA2.animate().move(-200).opacity(0).loop();
animation2
.during(function( pos ) {
document.getElementById("range_animation").value = pos;
if ( atEnd (pos) ) {
animation2.pause();
animation3 = rectB1.animate().move(200).opacity(1).loop();
animation4 = rectB2.animate().opacity(1).move(-200).loop();
animation4
.during(function( pos ) {
if (pos > 0) {
document.getElementById("range_animation").value = 1 + pos;
}
if (atEnd (pos)) {
animation4.pause();
}
});
animation3.during( function(pos) {
if (atEnd (pos)) {
animation3.pause();
}
});
}
});
animation1.during(function( pos ) {
if (atEnd (pos)) {
animation1.pause();
}
});
$('input#range_animation').on('input', function() {
$(this).trigger('change');
})
$('input#range_animation').on('change', function() {
var val = $( this).val(),
delta = 0;
if (lastTotalRange) {
delta = val - lastTotalRange;
}
if (val < 1) {
animation1.at(val).pause();
animation2.at(val).pause();
if (lastTotalRange > 1) {
animation3.at(.01);
animation4.at(.01);
}
}
else {
animation3.at(val - 1).pause();
animation4.at(val - 1).pause();
if (lastTotalRange < 1) {
animation1.at(.99);
animation1.at(.99);
}
}
// console.log('totalAnimationRange:change', delta, val);
lastTotalRange = val;
})
and the html
<div id="drawing"></div>
<input type="range" id="range_animation" min="0.00" max="1.99" step=".01" value=".01">
and the jsfiddle:
jsfiddle.net/voam/ob2bjfur/1/
Related
I hope someone can help me but I have just started learning javascript and I have been working on a quiz for a page of a learning website that I am helping to create. I have been asked to add a message that pops up at the end of the quiz but I can't seem to get it to work. Please excuse any terrible obvious mistakes as like I said I have only been looking into it for a couple of days.
I have a div in the html called message that I wanted to the message to appear.
This is the js I have so far. Any tips would be massively appreciated.
(function($) {
$.fn.emc = function(options) {
var defaults = {
key: [],
scoring: "normal",
progress: true
},
settings = $.extend(defaults,options),
$quizItems = $('[data-quiz-item]'),
$choices = $('[data-choices]'),
itemCount = $quizItems.length,
chosen = [],
$option = null,
$label = null;
emcInit();
if (settings.progress) {
var $bar = $('#emc-progress'),
$inner = $('<div id="emc-progress_inner"></div>'),
$perc = $('<span id="emc-progress_ind">0/'+itemCount+'</span>');
$bar.append($inner).prepend($perc);
}
function emcInit() {
$quizItems.each( function(index,value) {
var $this = $(this),
$choiceEl = $this.find('.choices'),
choices = $choiceEl.data('choices');
for (var i = 0; i < choices.length; i++) {
$option = $('<input name="'+index+'" id="'+index+'_'+i+'" type="radio">');
$label = $('<label for="'+index+'_'+i+'">'+choices[i]+'</label>');
$choiceEl.append($option).append($label);
$option.on( 'change', function() {
return getChosen();
});
}
});
}
function getChosen() {
chosen = [];
$choices.each( function() {
var $inputs = $(this).find('input[type="radio"]');
$inputs.each( function(index,value) {
if($(this).is(':checked')) {
chosen.push(index + 1);
}
});
});
getProgress();
}
function getProgress() {
var prog = (chosen.length / itemCount) * 100 + "%",
$submit = $('#emc-submit');
if (settings.progress) {
$perc.text(chosen.length+'/'+itemCount);
$inner.css({height: prog});
}
if (chosen.length === itemCount) {
$submit.addClass('ready-show');
$submit.click( function(){
return scoreNormal();
});
}
}
function scoreNormal() {
var wrong = [],
score = null,
$scoreEl = $('#emc-score');
for (var i = 0; i < itemCount; i++) {
if (chosen[i] != settings.key[i]) {
wrong.push(i);
}
}
$quizItems.each( function(index) {
var $this = $(this);
if ($.inArray(index, wrong) !== -1 ) {
$this.removeClass('item-correct').addClass('item-incorrect');
} else {
$this.removeClass('item-incorrect').addClass('item-correct');
}
});
score = ((itemCount - wrong.length) / itemCount).toFixed(2) * 100 + "%";
$scoreEl.text("You scored a "+score).addClass('new-score');
}
function print(message) {
document.write(message);
}
if (score===100){
print('congratulations');
}else if(score<=99){
print('Try Again');
}
}
}(jQuery));
$(document).emc({
key: ["1","2","1","1","1","1"]
});
Popup Message
form controls tags
template literal interpolation
nested ternaries
Event Delegation
CSS transform and transition driven by .class
Demo
Enter a number in the <input>. To close the popup message, click the X in the upper righthand corner.
$('#quiz').on('change', function(e) {
var score = parseInt($('#score').val(), 10);
var msg = `Your score is ${score}<sup>×</sup><br>`;
var remark = (score === 100) ? `Perfect, great job!`: (score < 100 && score >= 90) ? `Well done`: (score < 90 && score >= 80) ? `Not bad`: (score < 80 && score >= 70) ? `You can do better`:(score < 70 && score >= 60) ? `That's bad`: `Did you even try?`;
$('#msg legend').html(`${msg}${remark}`).addClass('newScore');
$('#msg legend').on('click', 'sup', function(e) {
$(this).parent().removeClass('newScore');
});
});
#msg legend {
position: absolute;
z-index: 1;
transform: scale(0);
transition: 0.6s;
}
#msg legend.newScore {
font-size:5vw;
text-align:center;
transform-origin: left bottom;
transform: scale(2) translate(0,70%);
transition: 0.8s;
}
#msg legend.newScore sup {
cursor:pointer
}
<form id='quiz'>
<fieldset id='msg'>
<legend></legend>
<input id='score' type='number' min='0' max='100'> Enter your test score in the range of 0 to 100
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
The code is meant to animated some text in a typing fashion. I want it to run once, but it keeps looping through the area of sentences. How would i go about stopping it looping through. The top of the code gathers the value of a input and puts into the array, this all works fine. It is just the looping i am having issues with.
var yourWord = document.getElementById("myText").value;
var yourtext = "I took the word " + yourWord + "...";
var infomation = [yourtext,
'I looked at the most relevant news article relating to it...',
'And created this piece of art from the words in the article! '
],
part,
i = 0,
offset = 0,
pollyLen = Polly.length,
forwards = true,
skip_count = 0,
skip_delay = 5,
speed = 100;
var wordflick = function () {
setInterval(function () {
if (forwards) {
if (offset >= infomation[i].length) {
++skip_count;
if (skip_count == skip_delay) {
forwards = false;
skip_count = 0;
}
}
} else {
if (offset == 0) {
forwards = true;
i++;
offset = 0;
if (i >= pollyLen) {
i = 0;
}
}
}
part = infomation[i].substr(0, offset);
if (skip_count == 0) {
if (forwards) {
offset++;
} else {
offset--;
}
}
$('#pollyInfo').text(part);
}, speed);
};
$(document).ready(function () {
wordflick();
});
Modify the line:
setInterval(function () {
into:
var interval = setInterval(function () {
and then clear the interval where you are setting i=0;
if (i >= pollyLen) {
i = 0;
}
to:
if (i >= pollyLen) {
i = 0;
clearInterval(interval);
}
This should do the job!
I need to make it working for all my elements under Which nothing should fade (images).
Currently works only for the div.logo tag
I guess at the moment the reason mine <h1> element is now allowing animations underneath is that when I am getting the .top(), .left() and so on, you are doing it for the last element in the list.
I need to get the borders of each element in the resulting list.
Any help would be much appreciated
Demo: http://jsfiddle.net/z9b8S/
JS:
function displayThese(selectorString) {
var $heading = $(selectorString);
var h1top = $heading.position().top;
var h1bottom = h1top + $heading.height();
var h1left = $heading.position().left;
var h1right = h1top + $heading.width();
var divs = $('li').filter(function () {
var $e = $(this);
var top = $e.position().top;
var bottom = top + $e.height();
var left = $e.position().left;
var right = left + $e.width();
return top > h1bottom || bottom < h1top || left > h1right || right < h1left;
});
return divs;
}
(function fadeInDiv() {
var divsToChange = displayThese('h1, div.logo');
var elem = divsToChange.eq(Math.floor(Math.random() * divsToChange.length));
if (!elem.is(':visible')) {
elem.prev().remove();
elem.animate({
opacity: 1
}, Math.floor(Math.random() * 1000), fadeInDiv);
} else {
elem.animate({
opacity: (Math.random() * 1)
}, Math.floor(Math.random() * 1000), function () {
window.setTimeout(fadeInDiv);
});
}
})();
$(window).resize(function () {
// Get items that do not change
var divs = $('li').not(displayThese());
divs.css({
opacity: 0.3
});
});
The script below creates a slider widget the takes a definition list and turns it into a slide deck. Each dt element is rotated via css to become the "spine", which is used to reveal that dt's sibling dd element.
What I'm trying to do is to enhance it so that I can have the option to remove the spines from the layout and just use forward and back buttons on either side of the slide deck. To do that, I set the dt's to display:none via CSS and use the code under the "Remove spine layout" comment to test for visible.
This works fine to remove the spines, now I need to dynamically create 2 absolutely positioned divs to hold the left and right arrow images, as well as attach a click handler to them.
My first problem is that my attempt to create the divs is not working.
Any help much appreciated.
jQuery.noConflict();
(function(jQuery) {
if (typeof jQuery == 'undefined') return;
jQuery.fn.easyAccordion = function(options) {
var defaults = {
slideNum: true,
autoStart: false,
pauseOnHover: true,
slideInterval: 5000
};
this.each(function() {
var settings = jQuery.extend(defaults, options);
jQuery(this).find('dl').addClass('easy-accordion');
// -------- Set the variables ------------------------------------------------------------------------------
jQuery.fn.setVariables = function() {
dlWidth = jQuery(this).width()-1;
dlHeight = jQuery(this).height();
if (!jQuery(this).find('dt').is(':visible')){
dtWidth = 0;
dtHeight = 0;
slideTotal = 0;
// Add an element to rewind to previous slide
var slidePrev = document.createElement('div');
slidePrev.className = 'slideAdv prev';
jQuery(this).append(slidePrev);
jQuery('.slideAdv.prev').css('background':'red','width':'50px','height':'50px');
// Add an element to advance to the next slide
var slideNext = document.createElement('div');
slideNext.className = 'slideAdv next';
jQuery(this).append(slideNext);
jQuery('.slideAdv.next').css('background':'green','width':'50px','height':'50px');
}
else
{
dtWidth = jQuery(this).find('dt').outerHeight();
if (jQuery.browser.msie){ dtWidth = jQuery(this).find('dt').outerWidth();}
dtHeight = dlHeight - (jQuery(this).find('dt').outerWidth()-jQuery(this).find('dt').width());
slideTotal = jQuery(this).find('dt').size();
}
ddWidth = dlWidth - (dtWidth*slideTotal) - (jQuery(this).find('dd').outerWidth(true)-jQuery(this).find('dd').width());
ddHeight = dlHeight - (jQuery(this).find('dd').outerHeight(true)-jQuery(this).find('dd').height());
};
jQuery(this).setVariables();
// -------- Fix some weird cross-browser issues due to the CSS rotation -------------------------------------
if (jQuery.browser.safari){ var dtTop = (dlHeight-dtWidth)/2; var dtOffset = -dtTop; /* Safari and Chrome */ }
if (jQuery.browser.mozilla){ var dtTop = dlHeight - 20; var dtOffset = - 20; /* FF */ }
if (jQuery.browser.msie){ var dtTop = 0; var dtOffset = 0; /* IE */ }
if (jQuery.browser.opera){ var dtTop = (dlHeight-dtWidth)/2; var dtOffset = -dtTop; } /* Opera */
// -------- Getting things ready ------------------------------------------------------------------------------
var f = 1;
var paused = false;
jQuery(this).find('dt').each(function(){
jQuery(this).css({'width':dtHeight,'top':dtTop,'margin-left':dtOffset});
// add unique id to each tab
jQuery(this).addClass('spine_' + f);
// add active corner
var corner = document.createElement('div');
corner.className = 'activeCorner spine_' + f;
jQuery(this).append(corner);
if(settings.slideNum == true){
jQuery('<span class="slide-number">'+f+'</span>').appendTo(this);
if(jQuery.browser.msie){
var slideNumLeft = parseInt(jQuery(this).find('.slide-number').css('left'));
if(jQuery.browser.version == 6.0 || jQuery.browser.version == 7.0){
jQuery(this).find('.slide-number').css({'bottom':'auto'});
slideNumLeft = slideNumLeft - 14;
jQuery(this).find('.slide-number').css({'left': slideNumLeft})
}
if(jQuery.browser.version == 8.0 || jQuery.browser.version == 9.0){
var slideNumTop = jQuery(this).find('.slide-number').css('bottom');
var slideNumTopVal = parseInt(slideNumTop) + parseInt(jQuery(this).css('padding-top')) - 20;
jQuery(this).find('.slide-number').css({'bottom': slideNumTopVal});
slideNumLeft = slideNumLeft - 10;
jQuery(this).find('.slide-number').css({'left': slideNumLeft})
jQuery(this).find('.slide-number').css({'marginTop': 10});
}
} else {
var slideNumTop = jQuery(this).find('.slide-number').css('bottom');
var slideNumTopVal = parseInt(slideNumTop) + parseInt(jQuery(this).css('padding-top'));
jQuery(this).find('.slide-number').css({'bottom': slideNumTopVal});
}
}
f = f + 1;
});
if(jQuery(this).find('.active').size()) {
jQuery(this).find('.active').next('dd').addClass('active');
} else {
jQuery(this).find('dt:first').addClass('active').next('dd').addClass('active');
}
jQuery(this).find('dt:first').css({'left':'0'}).next().css({'left':dtWidth});
jQuery(this).find('dd').css({'width':ddWidth,'height':ddHeight});
// -------- Functions ------------------------------------------------------------------------------
jQuery.fn.findActiveSlide = function() {
var i = 1;
this.find('dt').each(function(){
if(jQuery(this).hasClass('active')){
activeID = i; // Active slide
} else if (jQuery(this).hasClass('no-more-active')){
noMoreActiveID = i; // No more active slide
}
i = i + 1;
});
};
jQuery.fn.calculateSlidePos = function() {
var u = 2;
jQuery(this).find('dt').not(':first').each(function(){
var activeDtPos = dtWidth*activeID;
if(u <= activeID){
var leftDtPos = dtWidth*(u-1);
jQuery(this).animate({'left': leftDtPos});
if(u < activeID){ // If the item sits to the left of the active element
jQuery(this).next().css({'left':leftDtPos+dtWidth});
} else{ // If the item is the active one
jQuery(this).next().animate({'left':activeDtPos});
}
} else {
var rightDtPos = dlWidth-(dtWidth*(slideTotal-u+1));
jQuery(this).animate({'left': rightDtPos});
var rightDdPos = rightDtPos+dtWidth;
jQuery(this).next().animate({'left':rightDdPos});
}
u = u+ 1;
});
setTimeout( function() {
jQuery('.easy-accordion').find('dd').not('.active').each(function(){
jQuery(this).css({'display':'none'});
});
}, 400);
};
jQuery.fn.activateSlide = function() {
this.parent('dl').setVariables();
this.parent('dl').find('dd').css({'display':'block'});
this.parent('dl').find('dd.plus').removeClass('plus');
this.parent('dl').find('.no-more-active').removeClass('no-more-active');
this.parent('dl').find('.active').removeClass('active').addClass('no-more-active');
this.addClass('active').next().addClass('active');
this.parent('dl').findActiveSlide();
if(activeID < noMoreActiveID){
this.parent('dl').find('dd.no-more-active').addClass('plus');
}
this.parent('dl').calculateSlidePos();
};
jQuery.fn.rotateSlides = function(slideInterval, timerInstance) {
var accordianInstance = jQuery(this);
timerInstance.value = setTimeout(function(){accordianInstance.rotateSlides(slideInterval, timerInstance);}, slideInterval);
if (paused == false){
jQuery(this).findActiveSlide();
var totalSlides = jQuery(this).find('dt').size();
var activeSlide = activeID;
var newSlide = activeSlide + 1;
if (newSlide > totalSlides) {newSlide = 1; paused = true;}
jQuery(this).find('dt:eq(' + (newSlide-1) + ')').activateSlide(); // activate the new slide
}
}
// -------- Let's do it! ------------------------------------------------------------------------------
function trackerObject() {this.value = null}
var timerInstance = new trackerObject();
jQuery(this).findActiveSlide();
jQuery(this).calculateSlidePos();
if (settings.autoStart == true){
var accordianInstance = jQuery(this);
var interval = parseInt(settings.slideInterval);
timerInstance.value = setTimeout(function(){
accordianInstance.rotateSlides(interval, timerInstance);
}, interval);
}
jQuery(this).find('dt').not('active').click(function(){
var accordianInstance = jQuery(this); //JSB to fix bug with IE < 9
jQuery(this).activateSlide();
clearTimeout(timerInstance.value);
timerInstance.value = setTimeout(function(){
accordianInstance.rotateSlides(interval, timerInstance);
}, interval);
});
if (!(jQuery.browser.msie && jQuery.browser.version == 6.0)){
jQuery('dt').hover(function(){
jQuery(this).addClass('hover');
}, function(){
jQuery(this).removeClass('hover');
});
}
if (settings.pauseOnHover == true){
jQuery('dd').hover(function(){
paused = true;
}, function(){
paused = false;
});
}
});
};
})(jQuery);
Creating elements in jQuery is easy:
$newDiv = $('<div />');
$newDiv.css({
'position': 'absolute',
'top': '10px',
'left': '10px'
});
$newDiv.on('click', function() {
alert('You have clicked me');
});
$('#your_container').append($newDiv);
I'm looking to start and stop a loop with a set delay with a jQuery hover event. I've been trying to do it with "mouseover" and "mouseout" with no luck.
Example (odd psudocode):
Mouseover
Loop
Change text colour
Wait 100ms
Mouseout
Stop loop
I'm sure this is super easy, I just don't quite know how to structure it with JavaScript.
Thanks in advance.
This might work:
$(function(){
$('#test').hover(function(){
var self = $(this),
rnd = null,
col = null;
this.iid = setInterval(function(){
col = ['#'];
rnd = ~~(Math.random()*255);
col.push(rnd.toString(16).length < 2 ? '0' + rnd.toString(16) : rnd.toString(16));
col.push(rnd.toString(16).length < 2 ? '0' + rnd.toString(16) : rnd.toString(16));
col.push(rnd.toString(16).length < 2 ? '0' + rnd.toString(16) : rnd.toString(16));
self.css({backgroundColor: col.join('')});
}, 100);
}, function(){
if(this.iid){
clearInterval(this.iid);
delete this.iid;
}
});
});
See this in action: http://www.jsfiddle.net/YjC6y/19/
function rgb() {
var color = 'rgb(';
for (var i = 0; i < 3; i++) {
color += Math.floor(Math.random() * 255) + ',';
}
return color.replace(/\,$/, ')')
}
var loop = null;
$(function () {
$('#someid').hover(function () {
var $this = $(this);
loop = setInterval(function () {
$this.css({backgroundColor: rgb() });
}, 100);
}, function () {
clearInterval(loop);
});
});
try an example : http://jsbin.com/uraxe4
$("#yourElem").hover(
function () { /* mousenter */
$this = $(this);
// take note that the mouse is currently hovering
$this.data("isHovering", true);
// create an interval and store its ID in jQuery data
$this.data("loopId", setInterval(function () {
// only do something if we are still hovering
if ($this.data("isHovering")) {
$this.css("color", getRandomColorValue());
}
}, 100);
},
function () { /* mouseleave */
$this = $(this);
// take note that the mouse is no longer hovering
$this.data("isHovering", false);
// clear the interval that was set and delete the ID
if ($this.data("loopId")) {
clearInterval($this.data("loopId"));
$this.data("loopId", false);
}
}
)
changeColorTimerId = -1;
$('.box').hover(function(){
//mouseOver code here
changeColorTimerId = setInterval ( changeColor, 1000 );
},function(){
//mouseOut code here
if ( changeColorTimerId ){
clearInterval ( changeColorTimerId )
}
});
function changeColor(){
$(".box").css ( 'backgroundColor', '' + getRandomColor() );
}
function getRandomColor(){
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
working example here:
http://jsbin.com/etogi3/2