/**
|
* 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 +=
|
'<span class="' +
|
klass +
|
(i + 1) +
|
emptyclass +
|
'">' +
|
item +
|
"</span>" +
|
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 <br/> tag consistently across browsers,
|
// (*ahem* IE *ahem*), we replaces all <br/> 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);
|