Need Help: Trying to execute a javascript function repeatedly using <<repeat>>

0 votes
asked Mar 13 by puppetz87 (410 points)

Hello all, 

I'm trying to modify the "width" property of an element using javascript, and i want to trigger it repeatedly using a <<repeat>> macro.  My code is as follows:

A button that starts the loop to expand the element with an id of "ProgressBar":

<<button "Test">>
    <<repeat 100ms>>	
        <<if $Stop_expand is "False">>
            <<script>>setup.expand();<</script>>
        <<else>>
            <<stop>>
        <</if>>
    <</repeat>>
<</button>>

 A widget that stops the above repeat macro.

<<widget "Stop_expand">>
    <<set $Stop_expand to "True">>
<</widget>>

And finally the javascript function which (I think) Expands the ProgressBar element by 1% each time it is called.

setup.expand = function () {
	var elem  = document.getElementById("ProgressBar");
	var width;
	
	if (width = undefined) {
		var width = 1}
	else if (width >= 100) {
			new Wikifier(null, '<<Stop_expand>>');
	}
	else {
			width++;
			elem.style.width = width + '%';
	}
};

For some reason, nothing happens... and I'm not sure why. Can anyone help point out my mistake? >_< 

2 Answers

+1 vote
answered Mar 13 by greyelf (88,830 points)

There are a number of issues with your setup.expand() function:

1.  The = operator in the if (width = undefined) expression.
That operator is used for assignment, so the above expression is assigning the undefined value to the width variable. I believe you are trying to determine if the width variable has been defined, which it obviously is on the previous line, but anyway you should be using either the == or === comparison operators to do that.

2. You're incorrectly assuming that the width variable retains it's value between calls of the function.
Each time you call the function a new instance of the width variable is created, which means that even if point 1 was fixed (and your logic structure fixed) the value of the variable will never exceed 2.
eg. 1 at initialisation and +1 for the else clause.

3.  You need to persist the current width between calls of the function.
There are a number of ways you can do that, including using the value of the width CSS property itself.

Could you explain why you need to use TwineScript to animate an element with an ID of #progress (you don't make it clear exactly what type of element it is, which could influence then methods that can be used to animate it), and maybe someone can suggest a better way of implementing it.

commented Mar 18 by puppetz87 (410 points)

Sorry for the late reply.  Work keeps me busy :(

1.  The = operator in the if (width = undefined) expression.
That operator is used for assignment, so the above expression is assigning the undefined value to the width variable. I believe you are trying to determine if the width variable has been defined, which it obviously is on the previous line, but anyway you should be using either the == or === comparison operators to do that.

Ah nuts.... that was a typo.  Fixed.

 

 2. You're incorrectly assuming that the width variable retains it's value between calls of the function.
Each time you call the function a new instance of the width variable is created, which means that even if point 1 was fixed (and your logic structure fixed) the value of the variable will never exceed 2.
eg. 1 at initialisation and +1 for the else clause.

Ah okay... I sorta found out about this after reading a little... something about variables initialized within functions not being global and only persist within the function itself right?  Darn.

 

 

3.  You need to persist the current width between calls of the function.
There are a number of ways you can do that, including using the value of the widthCSS property itself.

Could you explain why you need to use TwineScript to animate an element with an ID of #progress (you don't make it clear exactly what type of element it is, which could influence then methods that can be used to animate it), and maybe someone can suggest a better way of implementing it.

I'm trying to make a game loop within TwineScript itself.  I'm actually trying to revamp my current "bad programming" which involves several <<repeat>> macros running at once once I read more into javascript single-threading and force yielding... but that's a topic for another day. 

The "#ProgressBar" element is basically just a white square/rectangle element with 0% width contained within a parent element with a set width to control its width (Edit: I defined the class properties of these in the global twine css stylesheets).  After the progress bar hits 100%, the "enemy" creature will execute an action (attack or heal or whatever), and after that, I need to get the width css property of the ProgressBar to restart at 0% again AND execute the width expanding function again.

The main problem I faced is that the rate at which this ProgressBar expands varies depending on other TwineScript variables ($enemy_atkspd, $enemy_slowed etc). So i can't use CSS timed animations (I don't know how to dynamically affect css animation timing properties using TwineScript) and I felt using too many State.active.variables.xxx in javascript to call upon the TwineScript variable library felt clunky.  

So I guess TLDR: I'm trying to make the game loop run in TwineScript to minimize clutter / timing issues.  #ProgressBar is just a 0% width white rectangle.

P.S. I've heard of the html5 canvas and the use of requestAnimationFrame() to get around single threading problems, but I'm still doing more research on that and how to get it to work within Twine and the context of my game, but I'll leave that for another day, or unless I have no choice. 

0 votes
answered Mar 15 by idling (10,470 points)

If you only want to expand an element from 0% width to 100% width, then you can do that with pure css:

@keyframes expand {
  0% {width:0%;}
  100% {width:100%;}
}

@-webkit-keyframes expand {
  0% {width:0%;}
  100% {width:100%;}
}

.expand {
  background-color: red;
  width: 0%;
  animation: expand 1s ease;
  -webkit-animation: expand 1s ease;
  animation-fill-mode: forwards;
}

 

commented Mar 15 by TheMadExile (44,270 points)

NOTE: Vendor prefixed properties generally continue to exist even after standards compliant properties are introduced and may have non-standard behavior.  Thus, since CSS cascades, you should always put vendor prefixed versions of properties before the standards compliant versions, so that the standards compliant properties "win".

For example:

@-webkit-keyframes expand {
	0% { width: 0%; }
	100% { width: 100%; }
}

@keyframes expand {
	0% { width: 0%; }
	100% { width: 100%; }
}

.expand {
	background-color: red;
	width: 0%;
	-webkit-animation: expand 1s ease;
	animation: expand 1s ease;
	animation-fill-mode: forwards;
}

 

commented Mar 15 by idling (10,470 points)
Oh - didn't know that. Thanks for the advice.
Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.
...