jquery.arctext.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /**
  2. * Arctext.js
  3. * A jQuery plugin for curved text
  4. * http://www.codrops.com
  5. *
  6. * Copyright 2011, Pedro Botelho / Codrops
  7. * Free to use under the MIT license.
  8. *
  9. * Date: Mon Jan 23 2012
  10. */
  11. (function( $, undefined ) {
  12. /*!
  13. * FitText.js 1.0
  14. *
  15. * Copyright 2011, Dave Rupert http://daverupert.com
  16. * Released under the WTFPL license
  17. * http://sam.zoy.org/wtfpl/
  18. *
  19. * Date: Thu May 05 14:23:00 2011 -0600
  20. */
  21. $.fn.fitText = function( kompressor, options ) {
  22. var settings = {
  23. 'minFontSize' : Number.NEGATIVE_INFINITY,
  24. 'maxFontSize' : Number.POSITIVE_INFINITY
  25. };
  26. return this.each(function() {
  27. var $this = $(this); // store the object
  28. var compressor = kompressor || 1; // set the compressor
  29. if ( options ) {
  30. $.extend( settings, options );
  31. }
  32. // Resizer() resizes items based on the object width divided by the compressor * 10
  33. var resizer = function () {
  34. $this.css('font-size', Math.max(Math.min($this.width() / (compressor*10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)));
  35. };
  36. // Call once to set.
  37. resizer();
  38. // Call on resize. Opera debounces their resize by default.
  39. $(window).resize(resizer);
  40. });
  41. };
  42. /*
  43. * Lettering plugin
  44. *
  45. * changed injector function:
  46. * add   for empty chars.
  47. */
  48. function injector(t, splitter, klass, after) {
  49. var a = t.text().split(splitter), inject = '', emptyclass;
  50. if (a.length) {
  51. $(a).each(function(i, item) {
  52. emptyclass = '';
  53. if(item === ' ') {
  54. emptyclass = ' empty';
  55. item=' ';
  56. }
  57. inject += '<span class="'+klass+(i+1)+emptyclass+'">'+item+'</span>'+after;
  58. });
  59. t.empty().append(inject);
  60. }
  61. }
  62. var methods = {
  63. init : function() {
  64. return this.each(function() {
  65. injector($(this), '', 'char', '');
  66. });
  67. },
  68. words : function() {
  69. return this.each(function() {
  70. injector($(this), ' ', 'word', ' ');
  71. });
  72. },
  73. lines : function() {
  74. return this.each(function() {
  75. var r = "eefec303079ad17405c889e092e105b0";
  76. // Because it's hard to split a <br/> tag consistently across browsers,
  77. // (*ahem* IE *ahem*), we replaces all <br/> instances with an md5 hash
  78. // (of the word "split"). If you're trying to use this plugin on that
  79. // md5 hash string, it will fail because you're being ridiculous.
  80. injector($(this).children("br").replaceWith(r).end(), r, 'line', '');
  81. });
  82. }
  83. };
  84. $.fn.lettering = function( method ) {
  85. // Method calling logic
  86. if ( method && methods[method] ) {
  87. return methods[ method ].apply( this, [].slice.call( arguments, 1 ));
  88. } else if ( method === 'letters' || ! method ) {
  89. return methods.init.apply( this, [].slice.call( arguments, 0 ) ); // always pass an array
  90. }
  91. $.error( 'Method ' + method + ' does not exist on jQuery.lettering' );
  92. return this;
  93. };
  94. /*
  95. * Arctext object.
  96. */
  97. $.Arctext = function( options, element ) {
  98. this.$el = $( element );
  99. this._init( options );
  100. };
  101. $.Arctext.defaults = {
  102. radius : 0, // the minimum value allowed is half of the word length. if set to -1, the word will be straight.
  103. dir : 1, // 1: curve is down, -1: curve is up.
  104. rotate : true, // if true each letter will be rotated.
  105. fitText : false // if you wanna try out the fitText plugin (http://fittextjs.com/) set this to true. Don't forget the wrapper should be fluid.
  106. };
  107. $.Arctext.prototype = {
  108. _init : function( options ) {
  109. this.options = $.extend( true, {}, $.Arctext.defaults, options );
  110. // apply the lettering plugin.
  111. this._applyLettering();
  112. this.$el.data( 'arctext', true );
  113. // calculate values
  114. this._calc();
  115. // apply transformation.
  116. this._rotateWord();
  117. // load the events
  118. this._loadEvents();
  119. },
  120. _applyLettering : function() {
  121. this.$el.lettering();
  122. if( this.options.fitText )
  123. this.$el.fitText();
  124. this.$letters = this.$el.find('span').css('display', 'inline-block');
  125. },
  126. _calc : function() {
  127. if( this.options.radius === -1 )
  128. return false;
  129. // calculate word / arc sizes & distances.
  130. this._calcBase();
  131. // get final values for each letter.
  132. this._calcLetters();
  133. },
  134. _calcBase : function() {
  135. // total word width (sum of letters widths)
  136. this.dtWord = 0;
  137. var _self = this;
  138. this.$letters.each( function(i) {
  139. var $letter = $(this),
  140. letterWidth = $letter.outerWidth( true );
  141. _self.dtWord += letterWidth;
  142. // save the center point of each letter:
  143. $letter.data( 'center', _self.dtWord - letterWidth / 2 );
  144. });
  145. // the middle point of the word.
  146. var centerWord = this.dtWord / 2;
  147. // check radius : the minimum value allowed is half of the word length.
  148. if( this.options.radius < centerWord )
  149. this.options.radius = centerWord;
  150. // total arc segment length, where the letters will be placed.
  151. this.dtArcBase = this.dtWord;
  152. // calculate the arc (length) that goes from the beginning of the first letter (x=0) to the end of the last letter (x=this.dtWord).
  153. // first lets calculate the angle for the triangle with base = this.dtArcBase and the other two sides = radius.
  154. var angle = 2 * Math.asin( this.dtArcBase / ( 2 * this.options.radius ) );
  155. // given the formula: L(ength) = R(adius) x A(ngle), we calculate our arc length.
  156. this.dtArc = this.options.radius * angle;
  157. },
  158. _calcLetters : function() {
  159. var _self = this,
  160. iteratorX = 0;
  161. this.$letters.each( function(i) {
  162. var $letter = $(this),
  163. // calculate each letter's semi arc given the percentage of each letter on the original word.
  164. dtArcLetter = ( $letter.outerWidth( true ) / _self.dtWord ) * _self.dtArc,
  165. // angle for the dtArcLetter given our radius.
  166. beta = dtArcLetter / _self.options.radius,
  167. // distance from the middle point of the semi arc's chord to the center of the circle.
  168. // this is going to be the place where the letter will be positioned.
  169. h = _self.options.radius * ( Math.cos( beta / 2 ) ),
  170. // angle formed by the x-axis and the left most point of the chord.
  171. alpha = Math.acos( ( _self.dtWord / 2 - iteratorX ) / _self.options.radius ),
  172. // angle formed by the x-axis and the right most point of the chord.
  173. theta = alpha + beta / 2,
  174. // distances of the sides of the triangle formed by h and the orthogonal to the x-axis.
  175. x = Math.cos( theta ) * h,
  176. y = Math.sin( theta ) * h,
  177. // the value for the coordinate x of the middle point of the chord.
  178. xpos = iteratorX + Math.abs( _self.dtWord / 2 - x - iteratorX ),
  179. // finally, calculate how much to translate each letter, given its center point.
  180. // also calculate the angle to rotate the letter accordingly.
  181. xval = 0| xpos - $letter.data( 'center' ),
  182. yval = 0| _self.options.radius - y,
  183. angle = ( _self.options.rotate ) ? 0| -Math.asin( x / _self.options.radius ) * ( 180 / Math.PI ) : 0;
  184. // the iteratorX will be positioned on the second point of each semi arc
  185. iteratorX = 2 * xpos - iteratorX;
  186. // save these values
  187. $letter.data({
  188. x : xval,
  189. y : ( _self.options.dir === 1 ) ? yval : -yval,
  190. a : ( _self.options.dir === 1 ) ? angle : -angle
  191. });
  192. });
  193. },
  194. _rotateWord : function( animation ) {
  195. if( !this.$el.data('arctext') ) return false;
  196. var _self = this;
  197. this.$letters.each( function(i) {
  198. var $letter = $(this),
  199. transformation = ( _self.options.radius === -1 ) ? 'none' : 'translateX(' + $letter.data('x') + 'px) translateY(' + $letter.data('y') + 'px) rotate(' + $letter.data('a') + 'deg)',
  200. transition = ( animation ) ? 'all ' + ( animation.speed || 0 ) + 'ms ' + ( animation.easing || 'linear' ) : 'none';
  201. $letter.css({
  202. '-webkit-transition' : transition,
  203. '-moz-transition' : transition,
  204. '-o-transition' : transition,
  205. '-ms-transition' : transition,
  206. 'transition' : transition
  207. })
  208. .css({
  209. '-webkit-transform' : transformation,
  210. '-moz-transform' : transformation,
  211. '-o-transform' : transformation,
  212. '-ms-transform' : transformation,
  213. 'transform' : transformation
  214. });
  215. });
  216. },
  217. _loadEvents : function() {
  218. if( this.options.fitText ) {
  219. var _self = this;
  220. $(window).on( 'resize.arctext', function() {
  221. _self._calc();
  222. // apply transformation.
  223. _self._rotateWord();
  224. });
  225. }
  226. },
  227. set : function( opts ) {
  228. if( !opts.radius &&
  229. !opts.dir &&
  230. opts.rotate === 'undefined' ) {
  231. return false;
  232. }
  233. this.options.radius = opts.radius || this.options.radius;
  234. this.options.dir = opts.dir || this.options.dir;
  235. if( opts.rotate !== undefined ) {
  236. this.options.rotate = opts.rotate;
  237. }
  238. this._calc();
  239. this._rotateWord( opts.animation );
  240. },
  241. destroy : function() {
  242. this.options.radius = -1;
  243. this._rotateWord();
  244. this.$letters.removeData('x y a center');
  245. this.$el.removeData('arctext');
  246. $(window).off('.arctext');
  247. }
  248. };
  249. var logError = function( message ) {
  250. if ( this.console ) {
  251. console.error( message );
  252. }
  253. };
  254. $.fn.arctext = function( options ) {
  255. if ( typeof options === 'string' ) {
  256. var args = Array.prototype.slice.call( arguments, 1 );
  257. this.each(function() {
  258. var instance = $.data( this, 'arctext' );
  259. if ( !instance ) {
  260. logError( "cannot call methods on arctext prior to initialization; " +
  261. "attempted to call method '" + options + "'" );
  262. return;
  263. }
  264. if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
  265. logError( "no such method '" + options + "' for arctext instance" );
  266. return;
  267. }
  268. instance[ options ].apply( instance, args );
  269. });
  270. }
  271. else {
  272. this.each(function() {
  273. var instance = $.data( this, 'arctext' );
  274. if ( !instance ) {
  275. $.data( this, 'arctext', new $.Arctext( options, this ) );
  276. }
  277. });
  278. }
  279. return this;
  280. };
  281. })( jQuery );