|
|
|
JS_INCLUDE = """ |
|
<link rel="stylesheet" |
|
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"> |
|
<script language="javascript"> |
|
function isInternetExplorer() { |
|
ua = navigator.userAgent; |
|
/* MSIE used to detect old browsers and Trident used to newer ones*/ |
|
return ua.indexOf("MSIE ") > -1 || ua.indexOf("Trident/") > -1; |
|
} |
|
|
|
/* Define the Animation class */ |
|
function Animation(frames, img_id, slider_id, interval, loop_select_id){ |
|
this.img_id = img_id; |
|
this.slider_id = slider_id; |
|
this.loop_select_id = loop_select_id; |
|
this.interval = interval; |
|
this.current_frame = 0; |
|
this.direction = 0; |
|
this.timer = null; |
|
this.frames = new Array(frames.length); |
|
|
|
for (var i=0; i<frames.length; i++) |
|
{ |
|
this.frames[i] = new Image(); |
|
this.frames[i].src = frames[i]; |
|
} |
|
var slider = document.getElementById(this.slider_id); |
|
slider.max = this.frames.length - 1; |
|
if (isInternetExplorer()) { |
|
// switch from oninput to onchange because IE <= 11 does not conform |
|
// with W3C specification. It ignores oninput and onchange behaves |
|
// like oninput. In contrast, Microsoft Edge behaves correctly. |
|
slider.setAttribute('onchange', slider.getAttribute('oninput')); |
|
slider.setAttribute('oninput', null); |
|
} |
|
this.set_frame(this.current_frame); |
|
} |
|
|
|
Animation.prototype.get_loop_state = function(){ |
|
var button_group = document[this.loop_select_id].state; |
|
for (var i = 0; i < button_group.length; i++) { |
|
var button = button_group[i]; |
|
if (button.checked) { |
|
return button.value; |
|
} |
|
} |
|
return undefined; |
|
} |
|
|
|
Animation.prototype.set_frame = function(frame){ |
|
this.current_frame = frame; |
|
document.getElementById(this.img_id).src = |
|
this.frames[this.current_frame].src; |
|
document.getElementById(this.slider_id).value = this.current_frame; |
|
} |
|
|
|
Animation.prototype.next_frame = function() |
|
{ |
|
this.set_frame(Math.min(this.frames.length - 1, this.current_frame + 1)); |
|
} |
|
|
|
Animation.prototype.previous_frame = function() |
|
{ |
|
this.set_frame(Math.max(0, this.current_frame - 1)); |
|
} |
|
|
|
Animation.prototype.first_frame = function() |
|
{ |
|
this.set_frame(0); |
|
} |
|
|
|
Animation.prototype.last_frame = function() |
|
{ |
|
this.set_frame(this.frames.length - 1); |
|
} |
|
|
|
Animation.prototype.slower = function() |
|
{ |
|
this.interval /= 0.7; |
|
if(this.direction > 0){this.play_animation();} |
|
else if(this.direction < 0){this.reverse_animation();} |
|
} |
|
|
|
Animation.prototype.faster = function() |
|
{ |
|
this.interval *= 0.7; |
|
if(this.direction > 0){this.play_animation();} |
|
else if(this.direction < 0){this.reverse_animation();} |
|
} |
|
|
|
Animation.prototype.anim_step_forward = function() |
|
{ |
|
this.current_frame += 1; |
|
if(this.current_frame < this.frames.length){ |
|
this.set_frame(this.current_frame); |
|
}else{ |
|
var loop_state = this.get_loop_state(); |
|
if(loop_state == "loop"){ |
|
this.first_frame(); |
|
}else if(loop_state == "reflect"){ |
|
this.last_frame(); |
|
this.reverse_animation(); |
|
}else{ |
|
this.pause_animation(); |
|
this.last_frame(); |
|
} |
|
} |
|
} |
|
|
|
Animation.prototype.anim_step_reverse = function() |
|
{ |
|
this.current_frame -= 1; |
|
if(this.current_frame >= 0){ |
|
this.set_frame(this.current_frame); |
|
}else{ |
|
var loop_state = this.get_loop_state(); |
|
if(loop_state == "loop"){ |
|
this.last_frame(); |
|
}else if(loop_state == "reflect"){ |
|
this.first_frame(); |
|
this.play_animation(); |
|
}else{ |
|
this.pause_animation(); |
|
this.first_frame(); |
|
} |
|
} |
|
} |
|
|
|
Animation.prototype.pause_animation = function() |
|
{ |
|
this.direction = 0; |
|
if (this.timer){ |
|
clearInterval(this.timer); |
|
this.timer = null; |
|
} |
|
} |
|
|
|
Animation.prototype.play_animation = function() |
|
{ |
|
this.pause_animation(); |
|
this.direction = 1; |
|
var t = this; |
|
if (!this.timer) this.timer = setInterval(function() { |
|
t.anim_step_forward(); |
|
}, this.interval); |
|
} |
|
|
|
Animation.prototype.reverse_animation = function() |
|
{ |
|
this.pause_animation(); |
|
this.direction = -1; |
|
var t = this; |
|
if (!this.timer) this.timer = setInterval(function() { |
|
t.anim_step_reverse(); |
|
}, this.interval); |
|
} |
|
</script> |
|
""" |
|
|
|
|
|
|
|
STYLE_INCLUDE = """ |
|
<style> |
|
.animation { |
|
display: inline-block; |
|
text-align: center; |
|
} |
|
input[type=range].anim-slider { |
|
width: 374px; |
|
margin-left: auto; |
|
margin-right: auto; |
|
} |
|
.anim-buttons { |
|
margin: 8px 0px; |
|
} |
|
.anim-buttons button { |
|
padding: 0; |
|
width: 36px; |
|
} |
|
.anim-state label { |
|
margin-right: 8px; |
|
} |
|
.anim-state input { |
|
margin: 0; |
|
vertical-align: middle; |
|
} |
|
</style> |
|
""" |
|
|
|
|
|
|
|
DISPLAY_TEMPLATE = """ |
|
<div class="animation"> |
|
<img id="_anim_img{id}"> |
|
<div class="anim-controls"> |
|
<input id="_anim_slider{id}" type="range" class="anim-slider" |
|
name="points" min="0" max="1" step="1" value="0" |
|
oninput="anim{id}.set_frame(parseInt(this.value));"> |
|
<div class="anim-buttons"> |
|
<button title="Decrease speed" aria-label="Decrease speed" onclick="anim{id}.slower()"> |
|
<i class="fa fa-minus"></i></button> |
|
<button title="First frame" aria-label="First frame" onclick="anim{id}.first_frame()"> |
|
<i class="fa fa-fast-backward"></i></button> |
|
<button title="Previous frame" aria-label="Previous frame" onclick="anim{id}.previous_frame()"> |
|
<i class="fa fa-step-backward"></i></button> |
|
<button title="Play backwards" aria-label="Play backwards" onclick="anim{id}.reverse_animation()"> |
|
<i class="fa fa-play fa-flip-horizontal"></i></button> |
|
<button title="Pause" aria-label="Pause" onclick="anim{id}.pause_animation()"> |
|
<i class="fa fa-pause"></i></button> |
|
<button title="Play" aria-label="Play" onclick="anim{id}.play_animation()"> |
|
<i class="fa fa-play"></i></button> |
|
<button title="Next frame" aria-label="Next frame" onclick="anim{id}.next_frame()"> |
|
<i class="fa fa-step-forward"></i></button> |
|
<button title="Last frame" aria-label="Last frame" onclick="anim{id}.last_frame()"> |
|
<i class="fa fa-fast-forward"></i></button> |
|
<button title="Increase speed" aria-label="Increase speed" onclick="anim{id}.faster()"> |
|
<i class="fa fa-plus"></i></button> |
|
</div> |
|
<form title="Repetition mode" aria-label="Repetition mode" action="#n" name="_anim_loop_select{id}" |
|
class="anim-state"> |
|
<input type="radio" name="state" value="once" id="_anim_radio1_{id}" |
|
{once_checked}> |
|
<label for="_anim_radio1_{id}">Once</label> |
|
<input type="radio" name="state" value="loop" id="_anim_radio2_{id}" |
|
{loop_checked}> |
|
<label for="_anim_radio2_{id}">Loop</label> |
|
<input type="radio" name="state" value="reflect" id="_anim_radio3_{id}" |
|
{reflect_checked}> |
|
<label for="_anim_radio3_{id}">Reflect</label> |
|
</form> |
|
</div> |
|
</div> |
|
|
|
|
|
<script language="javascript"> |
|
/* Instantiate the Animation class. */ |
|
/* The IDs given should match those used in the template above. */ |
|
(function() {{ |
|
var img_id = "_anim_img{id}"; |
|
var slider_id = "_anim_slider{id}"; |
|
var loop_select_id = "_anim_loop_select{id}"; |
|
var frames = new Array({Nframes}); |
|
{fill_frames} |
|
|
|
/* set a timeout to make sure all the above elements are created before |
|
the object is initialized. */ |
|
setTimeout(function() {{ |
|
anim{id} = new Animation(frames, img_id, slider_id, {interval}, |
|
loop_select_id); |
|
}}, 0); |
|
}})() |
|
</script> |
|
""" |
|
|
|
|
|
INCLUDED_FRAMES = """ |
|
for (var i=0; i<{Nframes}; i++){{ |
|
frames[i] = "{frame_dir}/frame" + ("0000000" + i).slice(-7) + |
|
".{frame_format}"; |
|
}} |
|
""" |
|
|