|
| 1 | +/* |
| 2 | + * Vide - v0.1.0 |
| 3 | + * Easy as hell jQuery plugin for video backgrounds. |
| 4 | + * http://vodkabears.github.io/vide/ |
| 5 | + * |
| 6 | + * Made by Ilya Makarov |
| 7 | + * Under MIT License |
| 8 | + */ |
| 9 | +;(function ($, window, document, navigator) { |
| 10 | + "use strict"; |
| 11 | + |
| 12 | + /** |
| 13 | + * Vide settings |
| 14 | + */ |
| 15 | + var pluginName = "vide", |
| 16 | + defaults = { |
| 17 | + volume: 1, |
| 18 | + playbackRate: 1, |
| 19 | + muted: true, |
| 20 | + loop: true, |
| 21 | + autoplay: true, |
| 22 | + position: "50% 50%" |
| 23 | + }; |
| 24 | + |
| 25 | + /** |
| 26 | + * Is iOs or Android? |
| 27 | + */ |
| 28 | + var iOS = /iPad|iPhone|iPod/i.test(navigator.userAgent), |
| 29 | + android = /Android/i.test(navigator.userAgent); |
| 30 | + |
| 31 | + /** |
| 32 | + * Special plugin object for instances. |
| 33 | + * @type {Object} |
| 34 | + */ |
| 35 | + $[pluginName] = { |
| 36 | + lookup: [] |
| 37 | + }; |
| 38 | + |
| 39 | + /** |
| 40 | + * Parse string with options |
| 41 | + * @param str |
| 42 | + * @returns {Object} |
| 43 | + */ |
| 44 | + var parseOptions = function (str) { |
| 45 | + var obj = {}, clearedStr, arr; |
| 46 | + |
| 47 | + // remove spaces before and after delimiters |
| 48 | + clearedStr = str.replace(/\s*:\s*/g, ":").replace(/\s*,\s*/g, ","); |
| 49 | + |
| 50 | + // parse string |
| 51 | + arr = clearedStr.split(","); |
| 52 | + var i, len, val; |
| 53 | + for (i = 0, len = arr.length; i < len; i++) { |
| 54 | + arr[i] = arr[i].split(":"); |
| 55 | + val = arr[i][1]; |
| 56 | + |
| 57 | + // convert string value if it is like a boolean |
| 58 | + if (typeof val === "string" || val instanceof String) { |
| 59 | + val = val === "true" || (val === "false" ? false : val); |
| 60 | + } |
| 61 | + |
| 62 | + // convert string value if it is like a number |
| 63 | + if (typeof val === "string" || val instanceof String) { |
| 64 | + val = !isNaN(val) ? +val : val; |
| 65 | + } |
| 66 | + |
| 67 | + obj[arr[i][0]] = val; |
| 68 | + } |
| 69 | + |
| 70 | + return obj; |
| 71 | + }; |
| 72 | + |
| 73 | + /** |
| 74 | + * Parse position option |
| 75 | + * @param str |
| 76 | + * @returns {{x: *, y: *}} |
| 77 | + */ |
| 78 | + var parsePosition = function (str) { |
| 79 | + var args = str.split(" "); |
| 80 | + |
| 81 | + switch (args[0]) { |
| 82 | + case "left": |
| 83 | + args[0] = "0%"; |
| 84 | + break; |
| 85 | + case "center": |
| 86 | + args[0] = "50%"; |
| 87 | + break; |
| 88 | + case "right": |
| 89 | + args[0] = "100%"; |
| 90 | + break; |
| 91 | + default: |
| 92 | + break; |
| 93 | + } |
| 94 | + |
| 95 | + switch (args[1]) { |
| 96 | + case "top": |
| 97 | + args[1] = "0"; |
| 98 | + break; |
| 99 | + case "middle": |
| 100 | + args[1] = "50%"; |
| 101 | + break; |
| 102 | + case "bottom": |
| 103 | + args[1] = "100%"; |
| 104 | + break; |
| 105 | + default: |
| 106 | + break; |
| 107 | + } |
| 108 | + |
| 109 | + return { x: args[0], y: args[1] }; |
| 110 | + }; |
| 111 | + |
| 112 | + /** |
| 113 | + * Vide constructor |
| 114 | + * @param element |
| 115 | + * @param path |
| 116 | + * @param options |
| 117 | + * @constructor |
| 118 | + */ |
| 119 | + function Vide(element, path, options) { |
| 120 | + this.element = $(element); |
| 121 | + this._defaults = defaults; |
| 122 | + this._name = pluginName; |
| 123 | + |
| 124 | + // remove extension |
| 125 | + var index = path.lastIndexOf("."); |
| 126 | + path = path.slice(0, index < 0 ? path.length : index); |
| 127 | + |
| 128 | + this.settings = $.extend({}, defaults, options); |
| 129 | + this.path = path; |
| 130 | + |
| 131 | + this.init(); |
| 132 | + } |
| 133 | + |
| 134 | + /** |
| 135 | + * Initialization |
| 136 | + */ |
| 137 | + Vide.prototype.init = function () { |
| 138 | + var that = this; |
| 139 | + |
| 140 | + this.wrapper = $("<div>"); |
| 141 | + |
| 142 | + // Set video wrapper styles |
| 143 | + this.wrapper.css({ |
| 144 | + "position": "absolute", |
| 145 | + "z-index": -1, |
| 146 | + "top": 0, |
| 147 | + "left": 0, |
| 148 | + "bottom": 0, |
| 149 | + "right": 0, |
| 150 | + "overflow": "hidden", |
| 151 | + "-webkit-background-size": "cover", |
| 152 | + "-moz-background-size": "cover", |
| 153 | + "-o-background-size": "cover", |
| 154 | + "background-size": "cover", |
| 155 | + "background-repeat": "no-repeat", |
| 156 | + "background-position": "center center" |
| 157 | + }); |
| 158 | + |
| 159 | + // Set video poster |
| 160 | + $.get(this.path + ".png") |
| 161 | + .done(function () { |
| 162 | + that.wrapper.css("background-image", "url(" + that.path + ".png)"); |
| 163 | + }); |
| 164 | + $.get(this.path + ".jpg") |
| 165 | + .done(function () { |
| 166 | + that.wrapper.css("background-image", "url(" + that.path + ".jpg)"); |
| 167 | + }); |
| 168 | + $.get(this.path + ".gif") |
| 169 | + .done(function () { |
| 170 | + that.wrapper.css("background-image", "url(" + that.path + ".gif)"); |
| 171 | + }); |
| 172 | + |
| 173 | + // if parent element has a static position, make it relative |
| 174 | + if (this.element.css("position") === "static") { |
| 175 | + this.element.css("position", "relative"); |
| 176 | + } |
| 177 | + |
| 178 | + this.element.prepend(this.wrapper); |
| 179 | + |
| 180 | + if (!iOS && !android) { |
| 181 | + this.video = $("<video>" + |
| 182 | + "<source src='" + this.path + ".mp4' type='video/mp4'>" + |
| 183 | + "<source src='" + this.path + ".webm' type='video/webm'>" + |
| 184 | + "<source src='" + this.path + ".ogv' type='video/ogg'>" + |
| 185 | + "</video>"); |
| 186 | + |
| 187 | + // Disable visibility, while loading |
| 188 | + this.video.css("visibility", "hidden"); |
| 189 | + |
| 190 | + // Set video properties |
| 191 | + this.video.prop({ |
| 192 | + autoplay: this.settings.autoplay, |
| 193 | + loop: this.settings.loop, |
| 194 | + volume: this.settings.volume, |
| 195 | + muted: this.settings.muted, |
| 196 | + playbackRate: this.settings.playbackRate |
| 197 | + }); |
| 198 | + |
| 199 | + // Append video |
| 200 | + this.wrapper.append(this.video); |
| 201 | + |
| 202 | + // Video alignment |
| 203 | + var position = parsePosition(this.settings.position); |
| 204 | + this.video.css({ |
| 205 | + "margin": "auto", |
| 206 | + "position": "absolute", |
| 207 | + "z-index": -1, |
| 208 | + "top": position.y, |
| 209 | + "left": position.x, |
| 210 | + "-webkit-transform": "translate(-" + position.x + ", -" + position.y + ")", |
| 211 | + "-ms-transform": "translate(-" + position.x + ", -" + position.y + ")", |
| 212 | + "transform": "translate(-" + position.x + ", -" + position.y + ")" |
| 213 | + }); |
| 214 | + |
| 215 | + // resize video, when it's loaded |
| 216 | + this.video.bind("loadedmetadata." + pluginName, function () { |
| 217 | + that.video.css("visibility", "visible"); |
| 218 | + that.resize(); |
| 219 | + }); |
| 220 | + |
| 221 | + // resize event is available only for 'window', |
| 222 | + // use another code solutions to detect DOM elements resizing |
| 223 | + $(this.element).bind("resize." + pluginName, function () { |
| 224 | + that.resize(); |
| 225 | + }); |
| 226 | + } |
| 227 | + }; |
| 228 | + |
| 229 | + /** |
| 230 | + * Get video element of the background |
| 231 | + * @returns {HTMLVideoElement} |
| 232 | + */ |
| 233 | + Vide.prototype.getVideoObject = function () { |
| 234 | + return this.video ? this.video[0] : null; |
| 235 | + }; |
| 236 | + |
| 237 | + /** |
| 238 | + * Resize video background |
| 239 | + */ |
| 240 | + Vide.prototype.resize = function () { |
| 241 | + if (!this.video) { |
| 242 | + return; |
| 243 | + } |
| 244 | + |
| 245 | + // get native video size |
| 246 | + var videoHeight = this.video[0].videoHeight, |
| 247 | + videoWidth = this.video[0].videoWidth; |
| 248 | + |
| 249 | + // get wrapper size |
| 250 | + var wrapperHeight = this.wrapper.height(), |
| 251 | + wrapperWidth = this.wrapper.width(); |
| 252 | + |
| 253 | + if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) { |
| 254 | + this.video.css({ |
| 255 | + "width": wrapperWidth + 2, // +2 pixels to prevent empty space after transformation |
| 256 | + "height": "auto" |
| 257 | + }); |
| 258 | + } else { |
| 259 | + this.video.css({ |
| 260 | + "width": "auto", |
| 261 | + "height": wrapperHeight + 2 // +2 pixels to prevent empty space after transformation |
| 262 | + }); |
| 263 | + } |
| 264 | + }; |
| 265 | + |
| 266 | + /** |
| 267 | + * Destroy video background |
| 268 | + */ |
| 269 | + Vide.prototype.destroy = function () { |
| 270 | + this.element.unbind(pluginName); |
| 271 | + this.video.unbind(pluginName); |
| 272 | + delete $[pluginName].lookup[this.index]; |
| 273 | + this.element.removeData(pluginName); |
| 274 | + this.wrapper.remove(); |
| 275 | + }; |
| 276 | + |
| 277 | + /** |
| 278 | + * Plugin constructor |
| 279 | + * @param path |
| 280 | + * @param options |
| 281 | + * @returns {*} |
| 282 | + */ |
| 283 | + $.fn[pluginName] = function (path, options) { |
| 284 | + var instance; |
| 285 | + this.each(function () { |
| 286 | + instance = $.data(this, pluginName); |
| 287 | + if (instance) { |
| 288 | + // destroy plugin instance if exists |
| 289 | + instance.destroy(); |
| 290 | + } |
| 291 | + // create plugin instance |
| 292 | + instance = new Vide(this, path, options); |
| 293 | + instance.index = $[pluginName].lookup.push(instance) - 1; |
| 294 | + $.data(this, pluginName, instance); |
| 295 | + }); |
| 296 | + |
| 297 | + return this; |
| 298 | + }; |
| 299 | + |
| 300 | + $(document).ready(function () { |
| 301 | + // window resize event listener |
| 302 | + $(window).bind("resize." + pluginName, function () { |
| 303 | + for (var len = $[pluginName].lookup.length, instance, i = 0; i < len; i++) { |
| 304 | + instance = $[pluginName].lookup[i]; |
| 305 | + if (instance) { |
| 306 | + instance.resize(); |
| 307 | + } |
| 308 | + } |
| 309 | + }); |
| 310 | + |
| 311 | + // Auto initialization. |
| 312 | + // Add 'data-vide-bg' attribute with a path to the video without extension. |
| 313 | + // Also you can pass options throw the 'data-vide-options' attribute. |
| 314 | + // 'data-vide-options' must be like "muted: false, volume: 0.5". |
| 315 | + $(document).find("[data-" + pluginName + "-bg]").each(function (i, element) { |
| 316 | + var $element = $(element), |
| 317 | + options = $element.data(pluginName + "-options"), |
| 318 | + path = $element.data(pluginName + "-bg"); |
| 319 | + |
| 320 | + if (!options) { |
| 321 | + options = {}; |
| 322 | + } else { |
| 323 | + options = parseOptions(options); |
| 324 | + } |
| 325 | + |
| 326 | + $element[pluginName](path, options); |
| 327 | + }); |
| 328 | + }); |
| 329 | +})(jQuery || Zepto, window, document, navigator); |
0 commit comments