/** * Arctext.js * A jQuery plugin for curved text * http://www.codrops.com * * Copyright 2011, Pedro Botelho / Codrops * Free to use under the MIT license. * * Date: Mon Jan 23 2012 */ (function ($, undefined) { /*! * FitText.js 1.0 * * Copyright 2011, Dave Rupert http://daverupert.com * Released under the WTFPL license * http://sam.zoy.org/wtfpl/ * * Date: Thu May 05 14:23:00 2011 -0600 */ $.fn.fitText = function (kompressor, options) { var settings = { minFontSize: Number.NEGATIVE_INFINITY, maxFontSize: Number.POSITIVE_INFINITY, }; return this.each(function () { var $this = $(this); // store the object var compressor = kompressor || 1; // set the compressor if (options) { $.extend(settings, options); } // Resizer() resizes items based on the object width divided by the compressor * 10 var resizer = function () { $this.css( "font-size", Math.max( Math.min( $this.width() / (compressor * 10), parseFloat(settings.maxFontSize) ), parseFloat(settings.minFontSize) ) ); }; // Call once to set. resizer(); // Call on resize. Opera debounces their resize by default. $(window).resize(resizer); }); }; /* * Lettering plugin * * changed injector function: * add   for empty chars. */ function injector(t, splitter, klass, after) { var a = t.text().split(splitter), inject = "", emptyclass; if (a.length) { $(a).each(function (i, item) { emptyclass = ""; if (item === " ") { emptyclass = " empty"; item = " "; } inject += '' + item + "" + after; }); t.empty().append(inject); } } var methods = { init: function () { return this.each(function () { injector($(this), "", "char", ""); }); }, words: function () { return this.each(function () { injector($(this), " ", "word", " "); }); }, lines: function () { return this.each(function () { var r = "eefec303079ad17405c889e092e105b0"; // Because it's hard to split a
tag consistently across browsers, // (*ahem* IE *ahem*), we replaces all
instances with an md5 hash // (of the word "split"). If you're trying to use this plugin on that // md5 hash string, it will fail because you're being ridiculous. injector($(this).children("br").replaceWith(r).end(), r, "line", ""); }); }, }; $.fn.lettering = function (method) { // Method calling logic if (method && methods[method]) { return methods[method].apply(this, [].slice.call(arguments, 1)); } else if (method === "letters" || !method) { return methods.init.apply(this, [].slice.call(arguments, 0)); // always pass an array } $.error("Method " + method + " does not exist on jQuery.lettering"); return this; }; /* * Arctext object. */ $.Arctext = function (options, element) { this.$el = $(element); this._init(options); }; $.Arctext.defaults = { radius: 0, // the minimum value allowed is half of the word length. if set to -1, the word will be straight. dir: 1, // 1: curve is down, -1: curve is up. rotate: true, // if true each letter will be rotated. 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. }; $.Arctext.prototype = { _init: function (options) { this.options = $.extend(true, {}, $.Arctext.defaults, options); // apply the lettering plugin. this._applyLettering(); this.$el.data("arctext", true); // calculate values this._calc(); // apply transformation. this._rotateWord(); // load the events this._loadEvents(); }, _applyLettering: function () { this.$el.lettering(); if (this.options.fitText) this.$el.fitText(); this.$letters = this.$el.find("span").css("display", "inline-block"); }, _calc: function () { if (this.options.radius === -1) return false; // calculate word / arc sizes & distances. this._calcBase(); // get final values for each letter. this._calcLetters(); }, _calcBase: function () { // total word width (sum of letters widths) this.dtWord = 0; var _self = this; this.$letters.each(function (i) { var $letter = $(this), letterWidth = $letter.outerWidth(true); _self.dtWord += letterWidth; // save the center point of each letter: $letter.data("center", _self.dtWord - letterWidth / 2); }); // the middle point of the word. var centerWord = this.dtWord / 2; // check radius : the minimum value allowed is half of the word length. if (this.options.radius < centerWord) this.options.radius = centerWord; // total arc segment length, where the letters will be placed. this.dtArcBase = this.dtWord; // 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). // first lets calculate the angle for the triangle with base = this.dtArcBase and the other two sides = radius. var angle = 2 * Math.asin(this.dtArcBase / (2 * this.options.radius)); // given the formula: L(ength) = R(adius) x A(ngle), we calculate our arc length. this.dtArc = this.options.radius * angle; }, _calcLetters: function () { var _self = this, iteratorX = 0; this.$letters.each(function (i) { var $letter = $(this), // calculate each letter's semi arc given the percentage of each letter on the original word. dtArcLetter = ($letter.outerWidth(true) / _self.dtWord) * _self.dtArc, // angle for the dtArcLetter given our radius. beta = dtArcLetter / _self.options.radius, // distance from the middle point of the semi arc's chord to the center of the circle. // this is going to be the place where the letter will be positioned. h = _self.options.radius * Math.cos(beta / 2), // angle formed by the x-axis and the left most point of the chord. alpha = Math.acos( (_self.dtWord / 2 - iteratorX) / _self.options.radius ), // angle formed by the x-axis and the right most point of the chord. theta = alpha + beta / 2, // distances of the sides of the triangle formed by h and the orthogonal to the x-axis. x = Math.cos(theta) * h, y = Math.sin(theta) * h, // the value for the coordinate x of the middle point of the chord. xpos = iteratorX + Math.abs(_self.dtWord / 2 - x - iteratorX), // finally, calculate how much to translate each letter, given its center point. // also calculate the angle to rotate the letter accordingly. xval = 0 | (xpos - $letter.data("center")), yval = 0 | (_self.options.radius - y), angle = _self.options.rotate ? 0 | (-Math.asin(x / _self.options.radius) * (180 / Math.PI)) : 0; // the iteratorX will be positioned on the second point of each semi arc iteratorX = 2 * xpos - iteratorX; // save these values $letter.data({ x: xval, y: _self.options.dir === 1 ? yval : -yval, a: _self.options.dir === 1 ? angle : -angle, }); }); }, _rotateWord: function (animation) { if (!this.$el.data("arctext")) return false; var _self = this; this.$letters.each(function (i) { var $letter = $(this), transformation = _self.options.radius === -1 ? "none" : "translateX(" + $letter.data("x") + "px) translateY(" + $letter.data("y") + "px) rotate(" + $letter.data("a") + "deg)", transition = animation ? "all " + (animation.speed || 0) + "ms " + (animation.easing || "linear") : "none"; $letter .css({ "-webkit-transition": transition, "-moz-transition": transition, "-o-transition": transition, "-ms-transition": transition, transition: transition, }) .css({ "-webkit-transform": transformation, "-moz-transform": transformation, "-o-transform": transformation, "-ms-transform": transformation, transform: transformation, }); }); }, _loadEvents: function () { if (this.options.fitText) { var _self = this; $(window).on("resize.arctext", function () { _self._calc(); // apply transformation. _self._rotateWord(); }); } }, set: function (opts) { if (!opts.radius && !opts.dir && opts.rotate === "undefined") { return false; } this.options.radius = opts.radius || this.options.radius; this.options.dir = opts.dir || this.options.dir; if (opts.rotate !== undefined) { this.options.rotate = opts.rotate; } this._calc(); this._rotateWord(opts.animation); }, destroy: function () { this.options.radius = -1; this._rotateWord(); this.$letters.removeData("x y a center"); this.$el.removeData("arctext"); $(window).off(".arctext"); }, }; var logError = function (message) { if (this.console) { console.error(message); } }; $.fn.arctext = function (options) { if (typeof options === "string") { var args = Array.prototype.slice.call(arguments, 1); this.each(function () { var instance = $.data(this, "arctext"); if (!instance) { logError( "cannot call methods on arctext prior to initialization; " + "attempted to call method '" + options + "'" ); return; } if (!$.isFunction(instance[options]) || options.charAt(0) === "_") { logError("no such method '" + options + "' for arctext instance"); return; } instance[options].apply(instance, args); }); } else { this.each(function () { var instance = $.data(this, "arctext"); if (!instance) { $.data(this, "arctext", new $.Arctext(options, this)); } }); } return this; }; })(jQuery);