/**
* 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);