HTML5 + Javascript + CSS
Je l'ai donc fait il y a longtemps (la dernière date de modification du fichier était le 21/09/2012). Heureux de l'avoir gardé. Malheureusement, il ne prend en charge que 4 points de contrôle dans son état actuel, mais j'y travaille.
EDIT: Bien que l’interface utilisateur ne supporte que 4 points de contrôle, la fonction sous-jacente (animateConstruction
) prend en charge un nombre arbitraire de points de contrôle. Bien que je ne suggère pas de le faire pour plus de 10, car le code est très inefficace. (J'ai essayé avec 25 et j'ai dû supprimer l'onglet à l'aide du Gestionnaire des tâches.) Si cela compte comme une soumission valide, je ne prévois pas de réviser le code.
NOTE: J'étais un amateur naïf à l'époque. Le code est erroné à de nombreux niveaux (y compris le manque de points-virgules et l'utilisation de eval
).
Utiliser
Enregistrez le code sous forme de fichier .html et ouvrez-le dans Google Chrome ou JSfiddle.
Si vous avez besoin de 4 points de contrôle ou moins, entrez les paramètres à droite, puis choisissez "Mode de construction" et appuyez sur "Animation" en bas à gauche.
Si vous avez besoin de plus de points de contrôle, appelez la animateConstruction
fonction. Il prend comme argument un tableau de coordonnées (tableaux à 2 éléments). (par exemple animateConstruction([[0,0],[500,0],[0,500]]
). Notez que la zone de dessin est 500x500 et que le système de coordonnées suit l'élément de canevas HTML (origine en haut à gauche, axe des x pointant vers la droite, axe des y pointant vers le bas).
Pour le violon, j'ai ajouté une zone de texte en bas à gauche. Entrez des coordonnées séparées par des points-virgules (la valeur par défaut est un exemple) et appuyez sur Aller.
Différences dans la version Fiddle
- La zone de texte
- Étapes d'animation par défaut réduites à 100
- Les courbes secondaires sont désactivées par défaut
Code
<html>
<head>
<style>
span.h{
display: inline-block;
text-align: center;
text-decoration: underline;
font: bold 1em Arial;
}
input[type="color"]{
-webkit-appearance: button-bevel;
vertical-align: -7px;
width: 21px;
height: 27px;
}
input[type="color"][disabled]{background: #FFF}
td{position:relative; padding:1px; text-align:center}
table[class] td{text-align:left}
td.t{padding:1px 5px; width:46px;}
table input[type="checkbox"]{visibility:hidden}
tr:hover input[type="checkbox"]{visibility:visible}
</style>
<script type='text/javascript'>
function Bezier(c){
if(c.length==2) return function(t){return [c[0][0]+t*(c[1][0]-c[0][0]),c[0][1]+t*(c[1][1]-c[0][1])]}
else return function(t){return Bezier([Bezier(c.slice(0,-1))(t),Bezier(c.slice(1))(t)])(t)}
}
function Bezier2(f1,f2){
return function(t){return Bezier([f1(t),f2(t)])(t)}
}
//============================================
var c = null
var settings = {'guide':{'show':[true,true,true,true], 'color':['#EEEEEE','#00FF00','#0000FF','#FF00FF'], 'width':[10,1,1,1]}, 'curve':{'show':[true,true,true,true], 'color':['#EEEEEE','#00FF00','#0000FF','#FF00FF'], 'width':[10,3,3,3]}, 'main':{'show':true, 'color':'#FF0000', 'width':10}, 'sample': 100, 'steps':200, 'stepTime':10, 'mode':'Bezier', 'coords':[[0,500],[125,450],[125,0],[500,0]]}
var itv = 0
window.addEventListener('load',function(){
c = $('c').getContext('2d')
c.lineCap = 'round'
c.lineJoin = 'round'
draw(settings.coords,1)
},true)
function get(k,i){
var t = settings
if(k.constructor == Array) k.forEach(function(e){t = t[e]})
return t.length>i ? t[i] : t.slice(-1)[0]
}
function frame(coords){
c.strokeStyle = settings.curve.color[0]
c.lineWidth = settings.guide.width[0]
c.beginPath()
c.moveTo.apply(c,coords[0])
coords.slice(1).forEach(function(e){c.lineTo.apply(c,e)})
c.stroke()
}
function transf(c){
var t = []
c.forEach(function(e){t.push([e[0]+5,e[1]+5])})
return t
}
//============================================
function drawBezier(coords,t){
if(t===undefined) t = 1
coords = transf(coords)
c.clearRect(0,0,510,510)
frame(coords)
c.beginPath()
c.strokeStyle = settings.main.color
c.lineWidth = settings.main.width
c.moveTo.apply(c,coords[0])
for(var i=0;i<=t*settings.sample;i++) c.lineTo.apply(c,Bezier(coords)(i/settings.sample))
c.stroke()
}
function animateBezier(coords){
var s = settings.steps
var cur = ($('t').value==1 ? ($('t').value=$('T').innerHTML=(0).toFixed(3))*1 : $('t').value*s)+1
var b = drawBezier(coords,$('t').value*1)
itv = setInterval(function(){
$("T").innerHTML = ($("t").value = cur/s).toFixed(3)
drawBezier(coords,cur++/s,b)
if(cur>s) clearInterval(itv)
},settings.stepTime)
}
//============================================
function drawBezier2(coords,t){
if(t===undefined) t = 1
c.beginPath()
c.strokeStyle = get(['curve','color'],coords.length-1)
c.lineWidth = get(['curve','width'],coords.length-1)
c.moveTo.apply(c,coords[0])
for(var i=0;i<=t*100;i++) c.lineTo.apply(c,Bezier(coords)(i/100))
c.stroke()
}
function drawConstruction(coords,t,B){
coords = transf(coords)
if(t===undefined) t = 0.5
var b = B===undefined ? [[]] : B
coords.forEach(function(e){b[0].push(function(t){return e})})
c.clearRect(0,0,510,510)
frame(coords)
for(var i=1;i<coords.length;i++){
if(B===undefined) b.push([])
with(c){
for(var j=0;j<coords.length-i;j++){
if(B===undefined) b[i].push(Bezier2(b[i-1][j],b[i-1][j+1]))
if(i!=coords.length-1 && get(['curve','show'],i-1) || i==coords.length-1 && settings.main.show){
strokeStyle = i==coords.length-1?settings.main.color:get(['curve','color'],i-1)
lineWidth = i==coords.length-1?settings.main.width:get(['curve','width'],i-1)
beginPath()
moveTo.apply(c,b[i][j](0))
for(var k=0;k<=t*settings.sample;k++) lineTo.apply(c,b[i][j](k/settings.sample))
stroke()
}
if(i && i!=coords.length-1 && get(['guide','show'],i)){
strokeStyle = i==coords.length-1?settings.main.color:get(['guide','color'],i)
lineWidth = i==coords.length-1?settings.main.width:get(['guide','width'],i)
beginPath()
if(i!=coords.length-1) arc.apply(c,b[i][j](t).concat([settings.curve.width[0]/2,0,2*Math.PI]))
stroke()
}
}
if(i && i!=coords.length-1 && get(['guide','show'],i)){
beginPath()
moveTo.apply(c,b[i][0](t))
for(var j=1;j<coords.length-i;j++) lineTo.apply(c,b[i][j](t))
stroke()
}
}
}
return b
}
function animateConstruction(coords){
var s = settings.steps
var cur = ($('t').value==1 ? ($('t').value=$('T').innerHTML=(0).toFixed(3))*1 : $('t').value*s)+1
var b = drawConstruction(coords,$('t').value*1)
itv = setInterval(function(){
$("T").innerHTML = ($("t").value = cur/s).toFixed(3)
drawConstruction(coords,cur++/s,b)
if(cur>s) clearInterval(itv)
},settings.stepTime)
}
//============================================
function draw(coords,t){clearInterval(itv); return window['draw'+settings.mode](coords,t)}
function animate(coords){clearInterval(itv); return window['animate'+settings.mode](coords);}
//============================================
function $(id){return document.getElementById(id)}
function v(o,p){
for(var i in o){
var k = (p||[]).concat([i]).join('-')
var t
if((t = o[i].constructor) == Object || t == Array) v(o[i],[k])
else if(t = $(k)){
if(t.type=='checkbox') t.checked = o[i]
else if(t.type=='radio'){
for(var j=0, t=document.getElementsByName(t.name); j<t.length; j++) if(t[j].value == o[i]){
t[j].checked = true
break
}
}else t.value = o[i]
}else if(t = $((i==0?'x':'y') + p[0].slice(-1))) t.value = o[i]
}
}
document.addEventListener('load',function(){
v(settings)
$('t').setAttribute('step',1/settings.steps)
var t = document.getElementsByTagName('input')
for(i=0;i<t.length;i++) t[i].addEventListener('change',function(){
var t
if((t=this.id.split('-')).length > 1){
var t1 = function(T){
var t = 'settings'
T.forEach(function(e){t += '[' + (isNaN(e)?'"'+e+'"':e) +']'})
eval(t + '=' + (this.type=='text'?this.value:(this.type=='checkbox'?this.checked:'"'+this.value+'"')))
$(T.join('-')).value = this.value
}
t1.call(this,t)
if(t[0]=='curve' && t[1]=='color' && $('u').checked==true) t1.call(this,['guide'].concat(t.slice(1)))
}else if(this.id == 'u'){
for(i=0;t=$('guide-color-'+i);i++){
t.disabled = this.checked
t.value = settings.guide.color[i] = this.checked?settings.curve.color[i]:t.value
}
}else if(this.id == 't'){
$('T').innerHTML = (this.value*1).toFixed(3)
draw(settings.coords,this.value*1)
}else if(t = /([xy])(\d+)/.exec(this.id)) settings.coords[t[2]*1][t[1]=='x'?0:1] = this.value*1
else settings[this.id] = this.value
if(this.id == 'steps') $("t").setAttribute("step",1/settings.steps)
},true)
},true)
</script>
</head>
<body>
<canvas style='float:left' width='510' height='510' id='c'>
</canvas>
<div style='padding-left:550px; font-family:Arial'>
<span class='h' style='width:123px'>Control Points</span><br />
(<input type='text' id='x0' size='3' maxlength='3' />,<input type='text' id='y0' size='3' maxlength='3' />)<br />
(<input type='text' id='x1' size='3' maxlength='3' />,<input type='text' id='y1' size='3' maxlength='3' />)<br />
(<input type='text' id='x2' size='3' maxlength='3' />,<input type='text' id='y2' size='3' maxlength='3' />)<br />
(<input type='text' id='x3' size='3' maxlength='3' />,<input type='text' id='y3' size='3' maxlength='3' />)<br /><br />
<span class='h' style='width:200px'>Appearance</span><br />
<span style='font-weight:bold'>Guide lines</span><br />
<input type='checkbox' checked='checked' id='u' onchange='' /> Use curve colors<br />
<table style='border-collapse:collapse'>
<tr><td><input type='checkbox' id='guide-show-0' /></td><td><input type='color' id='guide-color-0' disabled='disabled' /></td><td class='t'>Frame</td><td><input type='text' id='guide-width-0' size='2' maxlength='2' /></td></tr>
<tr><td><input type='checkbox' id='guide-show-1' /></td><td><input type='color' id='guide-color-1' disabled='disabled' /></td><td class='t'>1</td><td><input type='text' id='guide-width-1' size='2' maxlength='2' /></td></tr>
<tr><td><input type='checkbox' id='guide-show-2' /></td><td><input type='color' id='guide-color-2' disabled='disabled' /></td><td class='t'>2</td><td><input type='text' id='guide-width-2' size='2' maxlength='2' /></td></tr>
<tr><td><input type='checkbox' id='guide-show-3' /></td><td><input type='color' id='guide-color-3' disabled='disabled' /></td><td class='t'>3</td><td><input type='text' id='guide-width-3' size='2' maxlength='2' /></td></tr>
</table>
<span style='font-weight:bold'>Curves</span>
<table style='border-collapse:collapse'>
<tr><td><input type='checkbox' id='curve-show-0' /></td><td><input type='color' id='curve-color-0' /></td><td class='t'>1</td><td><input type='text' id='curve-width-0' size='2' maxlength='2' /></td></td></tr>
<tr><td><input type='checkbox' id='curve-show-1' /></td><td><input type='color' id='curve-color-1' /></td><td class='t'>2</td><td><input type='text' id='curve-width-1' size='2' maxlength='2' /></td></td></tr>
<tr><td><input type='checkbox' id='curve-show-2' /></td><td><input type='color' id='curve-color-2' /></td><td class='t'>3</td><td><input type='text' id='curve-width-2' size='2' maxlength='2' /></td></td></tr>
<tr><td><input type='checkbox' id='curve-show-3' /></td><td><input type='color' id='curve-color-3' /></td><td class='t'>4</td><td><input type='text' id='curve-width-3' size='2' maxlength='2' /></td></td></tr>
<tr><td><input type='checkbox' id='main-show' /></td><td><input type='color' id='main-color' /></td><td class='t'>Main</td><td><input type='text' id='main-width' size='2' maxlength='2' /></td></td></tr>
</table><br />
<span class='h' style='width:300px'>Graphing & Animation</span><br />
<table class>
<tr><td>Sample points:</td><td><input type='text' id='sample' /></td></tr>
<tr><td>Animation steps:</td><td><input type='text' id='steps' /></td></tr>
<tr><td>Step time:</td><td><input type='text' id='stepTime' />ms</td></tr>
</table>
<div style='position:absolute; top:526px; left:8px; width:510px; height:100px;'>
<input type='range' id='t' max='1' min='0' style='width:450px' value='1' /> <span id='T' style='vertical-align: 6px'>1.000</span><br />
<input type='button' onclick='draw(settings.coords,$("t").value*1)' value='Draw' /><input type='button' onclick='animate(settings.coords)' value='Animate' />
<input type='radio' id='mode' name='mode' value='Bezier' />Basic Mode <input type='radio' id='mode' name='mode' value='Construction' />Construction Mode
</div>
</body>
</html>