Опубликован: 28.02.2016 | Доступ: свободный | Студентов: 1888 / 174 | Длительность: 11:48:00
Лекция 10:

Пишем свой плагин

< Лекция 9 || Лекция 10: 123 || Лекция 11 >

Easing

Теперь опять обратимся к easing’у – приведу пример произвольной функции, которой будет следовать анимация. Дабы особо не фантазировать – я взял пример из статьи на вездесущем хабре o анимации в MooTools фреймворке [http://habrahabr.ru/post/43379/] – наглядный пример с сердцебиением, которое описывается следующими функциями:


В расширении функционала easing нет ничего военного:

$.extend($.easing, {
/**
* Heart Beat
*
* @param x progress
* @param t current time
* @param b = 0
* @param c = 1
* @param d duration
* @link http://habrahabr.ru/blogs/mootools/43379/
*/
heart:function(x, t, b, c, d) {
if (x < 0.3) return Math.pow(x, 4) * 49.4;
if (x < 0.4) return 9 * x - 2.3;
if (x < 0.5) return -13 * x + 6.5;
if (x < 0.6) return 4 * x - 2;
if (x < 0.7) return 0.4;
if (x < 0.75) return 4 * x - 2.4;
if (x < 0.8) return -4 * x + 3.6;
if (x >= 0.8) return 1 - Math.sin(Math.acos(x));
}
});

Чуть-чуть пояснений, конструкция "$.extend({}, {})" "смешивает" объекты:

$.extend({name:"Anton"}, {location:"Kharkiv"});
>>>
{
name:"Anton",
location:"Kharkiv"
}
$.extend({name:"Anton", location:"Kharkiv"}, {location:"Kyiv"});
>>>
{
name:"Anton",
location:"Kyiv"
}

Таким образом мы "вмешиваем" новый метод к существующему объекту "$.easing"; согласно коду, наш метод принимает в качестве параметров следующие значения:

  • x – коэффициент прохождения анимации, изменяется от 0 до 1, дробное
  • t – время прохождение анимации от старта в ms
  • b – начальное значение = 0
  • c – конечное значение = 1
  • d – продолжительность анимации

Результат конечно интересен, но его можно ещё чуть-чуть расширить дополнительными функциями (развернем и скомбинируем):

heartIn: function (x, t, b, c, d) {
return $.easing.heart(x, t, b, c, d);
},
heartOut: function (x, t, b, c, d) {
return c - $.easing.heart(1 - x, t, b, c, d) + b;
},
heartInOut: function (x, t, b, c, d) {
if (t < d/2) return $.easing.heartIn(x, t, b, c, d);
return $.easing.heartOut(x, t, b, c, d);
}

Получим следующие производные функции:


Работать с данным творением надо следующим образом:

$("#my").animate({height:"+200px"}, 2000, "heartIn"); // вот оно

Пример работы данной функции можно пощупать на примере

<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Пример расширения объекта easing</title>
    <link rel="profile" href="http://gmpg.org/xfn/11"/>
    <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
    <link rel="stylesheet" href="css/styles.css"/>
    <style type="text/css">
        .heart{
            width:200px;
            height:200px;
            background:#fff;
            margin:0 auto;
            overflow:hidden;
            position: relative;
            border-radius: 4px;
            -moz-border-radius: 4px;
            -webkit-border-radius: 4px;
            margin-bottom: 4px;;
        }
        .heart img{
            position:absolute;
            margin:50px;
            width: 100px;
            height: 100px;
            border: 0
        }
        .heart p{
            position:absolute;
            width:200px;
            padding: 0;
            text-align: center;
            margin-top: 8px;
            font-weight: 700;
        }
        .block {
            height: 100px;
        }
        .target {
            height: 10px;
            background: #999999;
        }
    </style>
	<script type="text/javascript" src="js/jquery.js"></script>
	<script type="text/javascript" src="js/code.js"></script>
    <script type="text/javascript">
        $.extend($.easing, {
            /**
             * Heart Beat
             *
             * @param x
             * @param t current time
             * @param b begining
             * @param c change in value
             * @param d duration
             *
             * @link http://habrahabr.ru/blogs/mootools/43379/
             */
            heart: function (x, t, b, c, d) {
                if (x < 0.3)  return Math.pow(x, 4) * 49.4;
                if (x < 0.4)  return 9 * x - 2.3;
                if (x < 0.5)  return -13 * x + 6.5;
                if (x < 0.6)  return 4 * x - 2;
                if (x < 0.7)  return 0.4;
                if (x < 0.75) return 4 * x - 2.4;
                if (x < 0.8)  return -4 * x + 3.6;
                if (x >= 0.8) return 1 - Math.sin(Math.acos(x));
            },
            heartIn: function (x, t, b, c, d) {
                return $.easing.heart(x, t, b, c, d);
            },
            heartOut: function (x, t, b, c, d) {
                return c - $.easing.heart(1 - x, t, b, c, d) + b;
            },
            heartInOut: function (x, t, b, c, d) {
                if (t < d/2) return $.easing.heartIn(x, t, b, c, d);
                return $.easing.heartOut(x, t, b, c, d);
            }
        });
        $(function(){
            $('.heart').click(function(){
                var easing = $(this).data('easing');
                $(this).find('img')
                        .animate({width:"-=100px",height:"-=100px",left:"+=50px",top:"+=50px"},3000, easing)
                        .animate({width:"+=100px",height:"+=100px",left:"-=50px",top:"-=50px"},200);
            });
        });
    </script>
</head>
<body>
    <div id="content" class="wrapper box">
        <menu>
            <a href="index.html" title="go prev" class="button alignleft" rel="prev">← Back </a>
            <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
            <hr/>
            <pre><code>$(<span>'.target'</span>).animate({height:<span>'+=100px'</span>}, 2000, <span>'heartIn'</span>)</code></pre>
            <button type="button" class="code">Run Code</button>
            <pre><code>$(<span>'.target'</span>).animate({height:<span>'+=100px'</span>}, 2000, <span>'heartOut'</span>)</code></pre>
            <button type="button" class="code">Run Code</button>
            <pre><code>$(<span>'.target'</span>).animate({height:<span>'+=100px'</span>}, 2000, <span>'heartInOut'</span>)</code></pre>
            <button type="button" class="code">Run Code</button>
        </menu>
        <header>
            <h1>Пример расширять объект easing</h1>
            <h2>Пробуем, играемся, и мотаем на ус (покликайте по сердцам)</h2>
        </header>
        <article>
            <div class="block">
                <div class="target"></div>
            </div>
            <div class="heart" data-easing="heartIn">
                <p>heartIn</p>
                <img src="images/heart.png" alt="Heart" />
            </div>
            <div class="heart" data-easing="heartOut">
                <p>heartOut</p>
                <img src="images/heart.png" alt="Heart" />
            </div>
            <div class="heart" data-easing="heartInOut">
                <p>heartInOut</p>
                <img src="images/heart.png" alt="Heart" />
            </div>
        </article>
        <footer>
            ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
        </footer>
        <script type="text/javascript">
            var _gaq = _gaq || [];
            _gaq.push(['_setAccount', 'UA-1669896-2']);
            _gaq.push(['_trackPageview']);
            (function() {
             var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
             ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
             var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
            })();
         </script>
	</div>
</body>
</html>

Sizzle

Когда я рассказывал о Sizzle я решил вас не грузить возможностями по расширению библиотеки, но вот время настало… В Sizzle можно расширять много чего:

  • Sizzle.selectors.match
  • Sizzle.selectors.find
  • Sizzle.selectors.filter
  • Sizzle.selectors.attrHandle
  • Sizzle.selectors.pseudos

Но браться мы будем лишь за расширение псевдо-селекторов, наподобие:

$("div:animated"); // поиск анимированных элементов
$("div:hidden"); // поиск скрытых элементов div
$("div:visible"); // поиск видимых элементов div

Почему я привел только эти фильтры? Всё просто — только они не входят в Sizzle, и относятся лишь к jQuery, именно такие плагины мы будем тренироваться разрабатывать. Начнем с кода фильтра ":hidden":

// пример для расширения Sizzle внутри jQuery
// для расширения самого Sizzle нужен чуть-чуть другой код
jQuery.expr.filters.hidden = function( elem ) {
// проверяем ширину и высоту каждого элемента в выборке
return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};

Выглядит данный код несложно, но, пожалуй, я таки дам каркас для нового фильтра и добавлю чуть-чуть пояснений:

$.extend($.expr[':'], {
/**
* @param element DOM элемент
* @param i порядковый номер элемента
* @param match объект матчинга регулярного выражения
* @param elements массив всех найденных DOM элементов
*/
test: function(element, i, match, elements) {
/* тут будет наш код, и будет решать кто виноват */
return true || false; // выносим вердикт
}
})

Ну теперь попробуем решить следующую задачку:

Необходимо выделить ссылки в тексте в зависимости от её типа: внешняя, внутренняя или якорь

Для решения лучше всего подошли бы фильтры для селекторов следующего вида:

$("a:internal");
$("a:anchor");
$("a:external");

Поскольку "из коробки" данный функционал не доступен, мы напишем его сами, для этого нам понадобится не так уж и много :

$.extend($.expr[':'], {
// определения внешней ссылки
// нам понадобится лишь DOM Element
external: function(element) {
// а у нас ссылка?
if (element.tagName.toUpperCase() != 'A') return false;
// есть ли атрибут href
if (element.getAttribute('href')) {
var href = element.getAttribute('href');
// отсекаем ненужное
if ((href.indexOf('/') === 0) // внутренняя ссылка
|| (href.indexOf('#') === 0) // якорь
// наш домен по http:// или https://
|| (href.indexOf(window.location.hostname) === 7)
|| (href.indexOf(window.location.hostname) === 8)
) {
return false; // мимо
} else {
return true; // да, мы нашли внешние ссылки
}
} else {
return false;
}
}
})

Пример лишь для последнего ":external", рабочий код:

<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Пример создания фильтра для Sizzle</title>
    <link rel="profile" href="http://gmpg.org/xfn/11"/>
    <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
    <link rel="stylesheet" href="css/styles.css"/>
    <script type="text/javascript" src="js/jquery.js"></script>
	<script type="text/javascript" src="js/code.js"></script>
    <script>
        (function($, window, undefined){
            $.extend($.expr[':'], {
                /**
                 * Get internal elements
                 *
                 * @param element DOM Element
                 * @param i Integer Index
                 * @param match Object All matched data
                 * @param elements Array of Dom Elements
                 */
                internal: function(element, i, match, elements) {
                    // internal links
                    if (element.tagName.toUpperCase() != 'A') return false;
                    if (element.getAttribute('href')) {
                        if ((element.getAttribute('href').indexOf('/') === 0)
                            || (element.getAttribute('href').indexOf(window.location.hostname) === 7) // http://...
                            || (element.getAttribute('href').indexOf(window.location.hostname) === 8) // https://...
                            ) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return false;
                    }
                },
                anchor: function(element) {
                    // anchor only
                    if (element.tagName.toUpperCase() != 'A') return false;
                    if (element.getAttribute('href')) {
                        return (element.getAttribute('href').indexOf('#') === 0);
                    } else {
                        return false;
                    }
                },
                external: function(element) {
                    // external link
                    if (element.tagName.toUpperCase() != 'A') return false;
                    if (element.getAttribute('href')) {
                        if ((element.getAttribute('href').indexOf('/') === 0)
                            || (element.getAttribute('href').indexOf('#') === 0)                      // #anchor
                            || (element.getAttribute('href').indexOf(window.location.hostname) === 7) // http://...
                            || (element.getAttribute('href').indexOf(window.location.hostname) === 8) // https://...
                                ) {
                            return false;
                        } else {
                            return true;
                        }
                    } else {
                        return false;
                    }
                }
            })
        })(jQuery, window);
    </script>
</head>
<body>
    <div id="content" class="wrapper box">
        <menu>
            <a href="index.html" title="go prev" class="button alignleft" rel="prev">← Back</a>
            <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
            <hr/>
			<pre><code>$(<span>'a:internal'</span>).css({
    color:<span>'#ff5555'</span>,
    fontSize:<span>'24px'</span>
});</code></pre>
            <button type="button" class="code">Run Code</button>
			<pre><code>$(<span>'a:anchor'</span>).css({
    color:<span>'#33aa33'</span>,
    fontSize:<span>'24px'</span>
});</code></pre>
            <button type="button" class="code">Run Code</button>
			<pre><code>$(<span>'a:external'</span>).css({
    color:<span>'#5555ff'</span>,
    fontSize:<span>'24px'</span>
});</code></pre>
            <button type="button" class="code">Run Code</button>
        </menu>
        <header>
            <h1>Пример создания фильтра для Sizzle</h1>
            <h2>Пробуем найти элементы по своему фильтру</h2>
        </header>
        <article>
            <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut lacinia quam nec enim scelerisque porta.
            In ut lorem ipsum. <a href="#">Proin iaculis viverra rutrum</a>. Maecenas quis ante enim. Vestibulum ante ipsum primis
            in faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum luctus tristique feugiat. Morbi
            dictum est dolor, at condimentum nisl. Ut id nunc augue, at luctus enim. Phasellus urna nunc, aliquam
            sit amet rutrum ac, imperdiet in nunc. Cras mattis massa et est sodales ac auctor mi sagittis. Fusce
            elementum ultrices nunc, eu scelerisque massa sodales quis. Aliquam bibendum accumsan nibh ut blandit.
            Vestibulum suscipit leo vitae magna tincidunt pharetra. Sed consequat, ligula non pretium ultrices,
            ligula enim venenatis urna, in scelerisque urna turpis vel neque. Praesent nibh urna, eleifend et
            fringilla eu, pellentesque sit amet nisl.
            </p>
            <p>
            <a href="http://google.com">Praesent venenatis dictum ante</a>, et viverra diam ultrices et. Maecenas egestas augue eget nibh
            consequat tempus. Quisque facilisis ipsum non mi molestie faucibus. Integer sapien est, mattis
            eu suscipit nec, auctor id dolor. In hac habitasse platea dictumst. Duis adipiscing tristique
            ipsum, ut tempus eros porta ut. Nulla enim quam, auctor ut venenatis at, pharetra eget enim.
            Nam suscipit quam eu erat adipiscing aliquam. Etiam aliquet sagittis viverra. <a href="http://anton.shevchuk.name/jquery/">Ut ultricies</a>,
            neque nec bibendum commodo, sem arcu lobortis nunc, ut luctus tellus erat at sem.
            </p>
            <p>
            Aliquam erat volutpat. <a href="/index.html">Nunc vel augue sagittis lacus aliquet consequat</a>. Integer eros risus,
            posuere non volutpat a, mattis et elit. Curabitur congue enim in purus sagittis eu ultrices sem
            sollicitudin. Morbi sit amet nibh sapien. Nulla facilisi. Integer neque purus, commodo eu tempor eu,
            interdum sit amet quam. <a href="http://hohli.com/">Vestibulum mollis ullamcorper ipsum sit amet imperdiet</a>. Nullam lorem nunc,
            scelerisque ac volutpat ut, elementum ut risus. Integer placerat turpis purus. Vestibulum quis turpis
            ut justo sodales vulputate. Duis lectus tellus, gravida non commodo eu, dictum a tellus.
            Praesent a nibh vel nisl sodales tincidunt at ac leo.
            </p>
        </article>
        <footer>
            ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
        </footer>
        <script type="text/javascript">
            var _gaq = _gaq || [];
            _gaq.push(['_setAccount', 'UA-1669896-2']);
            _gaq.push(['_trackPageview']);
            (function() {
             var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
             ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
             var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
            })();
         </script>
	</div>
</body>
</html>

ВСЕГДА используйте фильтр вместе с HTML тэгом который ищите:

$("tag:filter")

Это один из пунктов оптимизации работы с фильтрами jQuery, иначе ваш фильтр будет обрабатывать все DOM элементы на странице, а это может очень сильно сказаться на производительности. Если же у вас несколько тегов, то пишите уж лучше так — "$("tag1:filter, tag2:filter, tag3:filter")", или ещё лучше через вызов метода "filter()".

— "Sizzle Documentation" — скудненькая официальная документация [https://github.com/jquery/sizzle/wiki/Sizzle-Documentation]

< Лекция 9 || Лекция 10: 123 || Лекция 11 >
Наталья Маркова
Наталья Маркова
Ярослав Гаевой
Ярослав Гаевой

10 марта 2016 c 20:13 до 22:39 я сдавал экзамен. Однако, за два месяца статус не изменился: "Задание не проверено"

Когда ожидать проверки?