3434 font : inherit;
3535 vertical-align : top;
3636}
37+ canvas {
38+ border : 1px solid # bbb ;
39+ }
3740</ style >
3841
3942</ head >
@@ -59,17 +62,34 @@ <h1>box-shadow spread rounding</h1>
5962
6063 < label >
6164 Algorithm:
62- < select id ="algorithm " size ="6 ">
65+ < select id ="algorithm " size ="7 ">
6366 < option value ="old "> Old</ option >
67+ < option value ="linear "> Linear</ option >
6468 < option value ="arc "> Arc</ option >
6569 < option value ="hyp "> Hyperbolic</ option >
66- < option value ="cubic " selected > Cubic</ option >
67- < option value ="linear " > Linear </ option >
70+ < option value ="cubic "> Cubic</ option >
71+ < option value ="bezier " selected > Bezier </ option >
6872 < option value ="custom "> Custom</ option >
6973 </ select >
7074 </ label >
7175
72- < label > f(x) =
76+ < canvas id ="plot " width ="200 " height ="200 "> </ canvas >
77+
78+ < div id ="bezier_points ">
79+ < label > x1:
80+ < input id ="x1 " value =".25 " />
81+ </ label >
82+ < label > y1:
83+ < input id ="y1 " value =".25 " />
84+ </ label >
85+ < label > x2:
86+ < input id ="x2 " value =".25 " />
87+ </ label >
88+ < label > y2:
89+ < input id ="y2 " value ="1 " />
90+ </ label >
91+ </ div >
92+ < label > f(x) =
7393 < input id ="custom " value ="x < 1? 1-(x-1)*(x-1) : 1 " />
7494 </ label >
7595</ form >
@@ -79,6 +99,100 @@ <h1>box-shadow spread rounding</h1>
7999</ div >
80100
81101< script >
102+ CubicBezier = function ( x1 , y1 , x2 , y2 )
103+ {
104+ this . x1 = x1 ;
105+ this . y1 = y1 ;
106+ this . x2 = x2 ;
107+ this . y2 = y2 ;
108+ } ;
109+
110+ CubicBezier . prototype = {
111+ _solveQuadraticEquation : function ( a , b , c ) {
112+ var discriminant = Math . pow ( b , 2 ) - ( 4 * a * c ) ;
113+
114+ if ( discriminant < 0 ) {
115+ return [ ] ;
116+ }
117+ return [
118+ ( - b + Math . sqrt ( discriminant ) ) / ( 2 * a ) ,
119+ ( - b - Math . sqrt ( discriminant ) ) / ( 2 * a )
120+ ] ;
121+ } ,
122+
123+ _solveCubicEquation : function ( a , b , c , d ) {
124+ if ( ! a ) {
125+ return this . _solveQuadraticEquation ( b , c , d ) ;
126+ }
127+
128+ b /= a ;
129+ c /= a ;
130+ d /= a ;
131+
132+ var p = ( ( 3 * c ) - Math . pow ( b , 2 ) ) / 3 ;
133+ var q = ( ( 2 * Math . pow ( b , 3 ) ) - ( 9 * b * c ) + ( 27 * d ) ) / 27 ;
134+ var discriminant = Math . pow ( q / 2 , 2 ) + Math . pow ( p / 3 , 3 ) ;
135+
136+ if ( discriminant === 0 ) {
137+ return [ Math . pow ( q / 2 , 1 / 3 ) - ( b / 3 ) ] ;
138+ } else if ( discriminant > 0 ) {
139+ return [ Math . pow ( - ( q / 2 ) + Math . sqrt ( discriminant ) , 1 / 3 ) - Math . pow ( ( q / 2 ) + Math . sqrt ( discriminant ) , 1 / 3 ) - ( b / 3 ) ] ;
140+ } else {
141+ var r = Math . sqrt ( Math . pow ( - ( p / 3 ) , 3 ) ) ;
142+ var phi = Math . acos ( - ( q / ( 2 * Math . sqrt ( Math . pow ( - ( p / 3 ) , 3 ) ) ) ) ) ;
143+ var s = 2 * Math . pow ( r , 1 / 3 ) ;
144+
145+ return [
146+ s * Math . cos ( phi / 3 ) - b / 3 ,
147+ s * Math . cos ( ( phi + 2 * Math . PI ) / 3 ) - b / 3 ,
148+ s * Math . cos ( ( phi + 4 * Math . PI ) / 3 ) - b / 3
149+ ] ;
150+ }
151+ } ,
152+
153+ _solveCubicBezier : function ( p0 , p1 , p2 , p3 , x ) {
154+ p0 -= x ;
155+ p1 -= x ;
156+ p2 -= x ;
157+ p3 -= x ;
158+
159+ var roots = this . _solveCubicEquation (
160+ p3 - ( 3 * p2 ) + ( 3 * p1 ) - p0 ,
161+ ( 3 * p2 ) - ( 6 * p1 ) + ( 3 * p0 ) ,
162+ ( 3 * p1 ) - ( 3 * p0 ) ,
163+ p0
164+ ) ;
165+
166+ var result = 0 ;
167+ for ( var index = 0 ; index < roots . length ; index ++ ) {
168+ var root = Math . round ( roots [ index ] * 1000000000000000 ) / 1000000000000000 ;
169+ result = ( ( result <= root ) && ( root <= 1 ) ) ? root : result ;
170+ }
171+ return result ;
172+ } ,
173+
174+ _bezier : function ( t , p0 , p1 , p2 , p3 ) {
175+ return ( Math . pow ( 1 - t , 3 ) * p0 ) + ( 3 * Math . pow ( 1 - t , 2 ) * t * p1 ) + ( 3 * ( 1 - t ) * Math . pow ( t , 2 ) * p2 ) + ( Math . pow ( t , 3 ) * p3 ) ;
176+ } ,
177+
178+ x : function ( t ) {
179+ return this . _bezier ( t , 0 , this . x1 , this . x2 , 1 ) ;
180+ } ,
181+
182+ y : function ( t ) {
183+ return this . _bezier ( t , 0 , this . y1 , this . y2 , 1 ) ;
184+ } ,
185+
186+ tx : function ( x ) {
187+ return this . _solveCubicBezier ( 0 , this . x1 , this . x2 , 1 , x ) ;
188+ } ,
189+
190+ ty : function ( y ) {
191+ return this . _solveCubicBezier ( 0 , this . y1 , this . y2 , 1 , y ) ;
192+ }
193+ } ;
194+
195+
82196function $$ ( expr , con ) {
83197 return Array . prototype . slice . call ( ( con || document ) . querySelectorAll ( expr ) ) ;
84198}
@@ -123,6 +237,8 @@ <h1>box-shadow spread rounding</h1>
123237 }
124238} ;
125239
240+ Box . bezier = new CubicBezier ( x1 . value , y1 . value , x2 . value , y2 . value ) ;
241+
126242Box . constant = 4 ;
127243
128244Box . ratios = {
@@ -131,21 +247,25 @@ <h1>box-shadow spread rounding</h1>
131247 } ,
132248
133249 arc : function ( x ) {
134- return x < 1 ? Math . sqrt ( 1 - ( x - 1 ) * ( x - 1 ) ) : 1 ;
250+ return Math . sqrt ( 1 - ( x - 1 ) * ( x - 1 ) ) ;
135251 } ,
136252
137253 hyp : function ( x ) {
138254 return 1 - 1 / ( Box . constant * x + 1 ) ;
139255 } ,
140256
141257 cubic : function ( x ) {
142- return x < 1 ? 1 + Math . pow ( x - 1 , 3 ) : 1 ;
258+ return 1 + Math . pow ( x - 1 , 3 ) ;
143259 } ,
144260
145261 linear : function ( x ) {
146- return x < 1 ? x : 1 ;
262+ return x ;
147263 } ,
148264
265+ bezier : function ( x ) {
266+ return Box . bezier . y ( Box . bezier . tx ( x ) ) ;
267+ } ,
268+
149269 custom : function ( x ) {
150270 try {
151271 return eval ( custom . value ) ;
@@ -157,7 +277,7 @@ <h1>box-shadow spread rounding</h1>
157277} ;
158278
159279Box . spreadRounding = function ( r , spread ) {
160- var ratio = Box . ratio ( r / spread ) ;
280+ var ratio = ( r / spread ) < 1 ? Box . ratio ( r / spread ) : 1 ;
161281
162282 return r + spread * ratio ;
163283} ;
@@ -178,6 +298,8 @@ <h1>box-shadow spread rounding</h1>
178298 $$ ( '.box' ) . forEach ( function ( box ) {
179299 box . box . spread = + spread ;
180300 } ) ;
301+
302+ redrawPlot ( ) ;
181303} ;
182304
183305bradius . oninput = bradius . onchange = function ( ) {
@@ -186,19 +308,86 @@ <h1>box-shadow spread rounding</h1>
186308 $$ ( '.box' ) . forEach ( function ( box ) {
187309 box . box . borderRadius = + radius ;
188310 } ) ;
311+
312+ redrawPlot ( ) ;
189313} ;
190314
191315algorithm . onchange = function ( ) {
192316 Box . ratio = Box . ratios [ this . value ] ;
193317
194- custom . parentNode . style . visibility = this . value == 'custom' ? '' : 'hidden' ;
318+ bezier_points . style . display = this . value == 'bezier' ? '' : 'none' ;
319+ custom . parentNode . style . display = this . value == 'custom' ? '' : 'none' ;
195320
196321 Box . redrawAll ( ) ;
322+ redrawPlot ( ) ;
197323} ;
198324
325+ var plotContext = plot . getContext ( '2d' ) ;
326+ plotContext . lineWidth = 1 / plot . height ;
327+ plotContext . translate ( 0 , plot . height ) ;
328+ plotContext . scale ( plot . width , - plot . height ) ;
329+
199330algorithm . onchange ( ) ;
200331
201- custom . oninput = Box . redrawAll ;
332+ function redrawPlot ( ) {
333+ var context = plot . getContext ( '2d' ) ;
334+
335+ // draw function
336+ context . strokeStyle = "black" ;
337+ context . clearRect ( 0 , 0 , 1 , 1 ) ;
338+ context . beginPath ( ) ;
339+ context . moveTo ( 0 , Box . ratio ( 0 ) ) ;
340+ for ( var x = 0 ; x <= 1 ; x += .005 ) {
341+ context . lineTo ( x , Box . ratio ( x ) ) ;
342+ }
343+ context . stroke ( ) ;
344+
345+ // draw ratio
346+ var ratio = Math . min ( bradius . value / spread . value , 1 ) ;
347+ context . strokeStyle = "#ccc" ;
348+ context . beginPath ( ) ;
349+ context . moveTo ( ratio , 0 ) ;
350+ context . lineTo ( ratio , 1 ) ;
351+ context . stroke ( ) ;
352+ context . beginPath ( ) ;
353+ context . moveTo ( 0 , ratio < 1 ? Box . ratio ( ratio ) : 1 ) ;
354+ context . lineTo ( 1 , ratio < 1 ? Box . ratio ( ratio ) : 1 ) ;
355+ context . stroke ( ) ;
356+
357+ if ( 'bezier' == algorithm . value ) {
358+ context . strokeStyle = "red" ;
359+ context . beginPath ( ) ;
360+ context . moveTo ( 0 , 0 ) ;
361+ context . lineTo ( x1 . value , y1 . value ) ;
362+ context . stroke ( ) ;
363+ context . beginPath ( ) ;
364+ context . moveTo ( x1 . value , y1 . value + .025 ) ;
365+ context . arc ( x1 . value , y1 . value , .025 , 0 , 360 , false ) ;
366+ context . stroke ( ) ;
367+ context . strokeStyle = "green" ;
368+ context . beginPath ( ) ;
369+ context . moveTo ( 1 , 1 ) ;
370+ context . lineTo ( x2 . value , y2 . value ) ;
371+ context . stroke ( ) ;
372+ context . beginPath ( ) ;
373+ context . moveTo ( x2 . value , y2 . value + .025 ) ;
374+ context . arc ( x2 . value , y2 . value , .025 , 0 , 360 , false ) ;
375+ context . stroke ( ) ;
376+ }
377+ }
378+
379+ custom . oninput = function ( ) {
380+ Box . redrawAll ( ) ;
381+ redrawPlot ( ) ;
382+ } ;
383+
384+ x1 . oninput = y1 . oninput = x2 . oninput = y2 . oninput = function ( ) {
385+ if ( this . value < 0 ) this . value = 0 ;
386+ if ( this . value > 1 ) this . value = 1 ;
387+ Box . bezier = new CubicBezier ( x1 . value , y1 . value , x2 . value , y2 . value ) ;
388+ Box . redrawAll ( ) ;
389+ redrawPlot ( ) ;
390+ } ;
202391
203392//constant.oninput = function() {
204393// Box.constant = +this.value;
@@ -214,6 +403,7 @@ <h1>box-shadow spread rounding</h1>
214403} ) ;
215404
216405Box . redrawAll ( ) ;
406+ redrawPlot ( ) ;
217407</ script >
218408
219409</ body >
0 commit comments