Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!

Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 
Показаны сообщения с ярлыком js. Показать все сообщения
Показаны сообщения с ярлыком js. Показать все сообщения

понедельник, 17 ноября 2014 г.

Slidecaster на javascript

Сегодня будем писать слайдкастер. Эту такая штука, которая в браузере позволит совместить слайды с аудио. Вместо того, чтобы писать avisynth скрипт вручную, дергая постоянно virtualdub, я буду загружать весь контент в браузер и мышкой мувать ползунки - где когда какой слайд начать. Удобно - я такую фичу на SlideShare видел. Хочу такую же! Бесплатно и для всех докладчиков.

В начале реализаици любой фичи идет ее декомпозиция на подзадачи, каждая из которых гуглится - т.е. решение можно за 5-10 минут найти в сети и попробовать у себя в интеграции с существущим кодом. Самое первое, что мне приходит в голову - это сделать банальный прогрессбар и научиться управлять им со скрипта. Вперед! Гуглим что-то типа "jquery progressbar" и смотрим что предлагает гугл. И первый же пример мне подходит! берем волшебный copy past driven development и копируем исходник себе в файлик 1.html, оставляя пометку // спасибо за http://jqueryui.com/progressbar/ чтобы не терять связь с автором.

Вот он код. Что я сделал - поменял заголовок. У нас жеж наше приложение. Добавил комментарий и пути к скриптам сделал абсолютными.
<!doctype html>
<-- thanks for http://jqueryui.com/progressbar/ -->
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Slidecaster</title>
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
    <script src="https://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
    <script>
        $(function() {
            $( "#progressbar" ).progressbar({
                value: 37
            });
        });
    </script>
</head>
<body> 
    <div id="progressbar"></div>
</body>
</html>
Вот как это выглядит

Сразу после этого я создам git репозиторий в той же папке что и скрипт и отправлю изменения в файловый рай.

Шаг второй - добавить сверху на прогрессбарчик мувалку - наверное это будет какой-то дивчик, который я смогу мувать по прогрессбарчику. Вместе с этим позиция прогрессбарчика будет меняться в точку где размещен дивчик - ползунок.

Задачу так же можно дробить. Для начала просто отрисую поверх прогрессбара дивчик. Потом научусь его мувать. Потом свяжу позицию с состоянием на прогрессбарчике. Вроде как не дробится больше.

Что надо сделать - добавить новый дивчик, задать ему стиль.
<!doctype html>
<!-- thanks for http://jqueryui.com/progressbar/ -->
<html lang="en">
<head>
    ...
    <script>
     ...
    </script>
    <style>
        .move {
            width: 2px;
            height: 40px;
            background-color: black;
            cursor: e-resize;
            position: absolute;
        }
    </style>
</head>
<body> 
    <div id="progressbar"></div>
    <div class="move"></div>
</body>
</html>
Идем дальше. Дивчик надо заставить муваться. Для понимания как это сделать гуглим "jquery drag div" и первая же статья наша. Добавили в скрипт строчку
$(".move").draggable();
Но теперь надо ограничить передвижения дивчика. Как? Гуглим "jquery draggable only x" и первый же ответ stack overflow наш. Добавляем вызов функции draggable один параметр
$(".move").draggable({ axis: "x" });
Еще один маленький штришок - надо сделать чтобы дивчик мувался по прогрессбару а не под ним. Добавим еще одну строчку.
$(".move").css("top", $("#progressbar").position().top - 5);
$(".move").height($("#progressbar").height() + 10);
Как это делается я помню, так как неоднократно делал уже. Но если че загуглить можно было бы по "jquery position get/change". Надо бы сохраниться! И посмотреть как это выглядит.


Следующий шаг - влиять на прогрессбар. И тут начинается самое интересное, поскольку оказывается, что тот прогрессбар, который предоставляется jquery немного не попиксельно перемещается. Вот как-то так изменилась функция.
$(function() {
    $("#progressbar").progressbar({  
        value: 0,  // позицию по умолчанию в ноль - там где и курсор
        max: $("#progressbar").width()     // количество делений прогрессбара такое же как и ширина 
    });
    $(".move").css("top", $("#progressbar").position().top - 5);
    $(".move").height($("#progressbar").height() + 10);
    $(".move").draggable({ axis: "x" }); 
   
    // добавили новый обработчик драга дивчика при его перемещении - меняется и состояние прогрессбара
    var drag = function(event){
        var pos = $(".move").position().left - $('#progressbar').position().left;
        $("#progressbar").progressbar("value", pos);
    };
       $('body').bind('drag', drag);
});
Посмотреть как это выглядит можно по этой линке или ниже в iframe.


Продолжение следует! А пока можешь погуглить в чем дело :)

понедельник, 7 октября 2013 г.

Как я собираю кубик Рубика

Когда-то давно я ходил с кубиком Рубика везде. За пару месяцев отточил мастерство сборки до рекордного 1 минута 20 секунд.

Не так давно я заказал себе кубик для спидкубинга DaYan5 и снова стал его курить (в перерывах между работой,  по дороге из точки А в точку Б). Для начала потребовалось вспомнить все те алгоритмы, что я когда-то выбирал на просторах сети и сам придумывал.

Воспроизведение заняло некоторое время, а  потому я запишу алгоритмы тут, на будущее... Этими алгоритмами я вчера собирал в среднем за 2 минуты (руки уже не те).

Сразу скажу, кто хочет собирать на равне с мировыми рекордсменами, стоит погуглить другие алгоритмы - методика сборки другая и алгоритмов там больше запоминать надо. Я же не скорости ради, но для подпитки мозгов головоломками выкладываю свои алгоритмы тут.

Кстати, если интересно вот как быстро это происходит на соревнованиях


Для визуализации алгоритмов я решил не записывать на видео то как это делается, так же решил не скриншотить - все это очень много времени отнимает, а наработки практически невозможно повторно использовать.

Решил поискать в сети, что коллеги-программисты уже реализовали на javascript. И нашел вот эту работу. Спасибо Автору. Именно ее я и взял за основу.

И вот что получилось (этот html файл надо открыть в браузере)

понедельник, 18 февраля 2013 г.

Развернуть/свернуть блок в Moodle

В moodle есть такое понятие как block. Узенькая полосочка, в 180 пикселей шириной, слева и справа от основного контента. Много инфы там не разместишь. А хочется...

Переколбашивать дизайн и менять ширину влом, да и не спасет это сильно... Ну сколько выручу? Еще 40 пикселей? Мало!

Появилась идея подшаманить на javascript с jquery. Коль уж блок - это div и есть возможность править средствами moodle его контент (как html), то я могу туда вставить свой js код а в нем уже добавить кнопочку развернуть/сернуть.


Только все не так просто оказалось...  Столкнулся я с несколькими проблемами. Имя им: cross-browser, position-absolute, iframe-refresh и resize-window. По очереди. Для начала я заметил, что если поставить диву блока стиль position:fixed - то я смогу изменять размеры и позицию дива, так, чтобы он разворачивался и сворачивался


Пол часа работыи слава jQuery все было готово. Но тут я решил запустить Chrome и понял, что работа только начинается... В хроме оно вообще не так работало....Хак не прокатил...

Пришлось заморачиваться с position:absolute. Диву блока простая установка стиля position:absolute не нравилась - он вел себя как-то странно (не абсолютно). Пришлось вместе со стилем менять диву еще и родителя с какого-то там parent'a на body.

То есть я нажимаю expand и перещелкиваю родителя на body, ставлю position:absolute, меняю left/top/width/htight и опаля - все как я хотел. Потом, при нажатии на collapse я делаю все наоборот.

И тут оказалось, что appendChild клеит див в конец боковой панели блоков - нарушается сортировка. ЭЭЭххх. Пришлось при expand, до изменения у блока parent на body, создавать еще див-закладку, по которой потом можно будет вернуть див-блок на место. Плюс этот невидимый див-закладка будет держать физически место, дабы все последующие блоки в панели не сдвигались...

В общем наигрался я с left/top/width/height... И когда все выглядело одинаково в обоих браузерах вылезла еще одна трабла - оказывается если менять родителя диву, то в Firefox и Chrome (но не IE) содержимое того iframe, который расположен в диве, перегружается. Эй! Так дело не пойдет - оно кроме того, что лагает (время на загрузку календаря), так еще и сбрасывает пользователя в начальный режим календаря. Не гуд!!

Идея пришла после душа - надо сразу как загрузится страничка (еще до нажатия expand) сделать reparent на body + position:absolute, а потом, при expand/collapse, играться только с left/top/width/height....

Снова немного танцев у left/top/width/height и все получилось....

Последним (я надеюсь) демоном был resize-window. Дело в том, что если делать expand блока, а потом поменять размеры окна браузера, после чего сделать collapse (или наоборот collapse->resize->expand), то див-блок вернется не на свое место.

Еще немного танцев с left/top/width/height и все получилось....

Думаю задача более общая и кому-то могут пригодистя эти мои наработки. Функция при легкой доработке сможет развернуть/свернуть (влево+вниз) любой див на котрый ткнешь. А потому выложу тут код.

Код, который я вставляю в html view блока.
<p id="calendar-container">
<script src="/moodle/js/jquery-1.7.2.js" type="text/javascript"></script>
<script src="/moodle/js/block-resize.js" type="text/javascript"></script>
<script type="text/javascript">// <![CDATA[
    // урл ифрейма/ширина/высота развернутого дива/текущий див/див блока/подпись к линку
    setupBlock('http://google.calendar.com/blabla', 700, 500, 'calendar-container', 'inst406', 'calendar'); 
// ]]></script></p>
А вот и сам скрипт.
if (typeof(setupBlock) == 'undefined') {
    function setupBlock(iframeSrc, expandWidth, expandHeight, containerId, blockId, title) {
        var collapsedWidth = 190;
        var collapsedHeight = 360;
        
        var deltaWidth = 10;
        var deltaHeight = 53;
        
        // айдишки, элементы...
        // все строю относительно blockId, который уникален. Это надо для работы с двумя и больше блоками в режиме collapse/expand
        var container = $('#' + containerId);    
        var iframeId = 'iframe_' + blockId;
        var iframe = $('#' + iframeId);
        var block = $('#' + blockId);
        var blockMarker = null;

        // если случилось так, что метод вызовут два раза, мы тут тихонько выйдем
        if (iframe.length != 0) {
             return;
        }

        // метод ресайза ифрейма - оказывается его надо ресайзить по особенному
        var resizeIframe = function(width, height) {
            var element = document.getElementById(iframeId);
            element.width = width;
            element.height = height;
        }
        
        // добавляем динамически ифрейм и кнопочку 
        container.append('<iframe id="' + iframeId + '" src="' + iframeSrc + '" style="border: 0;" scrolling="no" frameborder="0"></iframe>');
        container.append('</br><a href="#"><span id="resize_' + iframeId + '">expand ' + title + '</span></a>');
        container.css({'margin-bottom':0}); // это ничего не значит
        resizeIframe(collapsedWidth-deltaWidth, collapsedHeight-deltaHeight); // ресайзим ифрейм

        // порция новеньких...
        var resizeLink = $('#resize_' + iframeId); 
        var iframe = $('#' + iframeId);

        // когда все загрузится
        $(document).ready(function() {           

            var collapsed = true; // изначально все свернуто                        
            resizeLink.click(function() { // но клик на линк меняет состояние на противоположное
                if (collapsed) {
                    expand();
                } else {
                    collapse();
                }
            });

            // тут сохраняем исходные размеры/координаты (абсолютные) блока
            var oldWidth = block.width();
            var oldHeight = block.height();  
            var oldLeft = block.offset().left;
            var oldTop = block.offset().top;     

            // два метода для подсчета left когда свернуто/развернуто
            var getLeftRestored = function() {
                return getLeftCollapsed() - expandWidth + oldWidth;
            };                        
            var getLeftCollapsed = function() {
                return blockMarker.offset().left;
            };            

            // был див где-то там в дереве, а мы его пересадим к корню body,
            // а на месте дива оставим закладку
            var absoluting = function() {
                // если закладки нет, создаем ее сразу после дива-блока
                if (blockMarker == null) { 
                    var blockMarkerId = 'marker_' + blockId;
                    block.after('<div id="' + blockMarkerId + '"></div>');
                    blockMarker = $("#" + blockMarkerId);
                }
                // закладка должнна занимать то же место, что сейчас занимает блок-див
                blockMarker.css({
                    left: oldWidth,
                    top: oldTop,
                    width: oldWidth, 
                    height: oldHeight,                     
                    'margin-left': block.css('margin-left'),
                    'margin-right': block.css('margin-right'),
                    'margin-top': block.css('margin-top'),
                    'margin-bottom': block.css('margin-bottom'),
                    'padding-left': block.css('padding-left'),
                    'padding-right': block.css('padding-right'),
                    'padding-top': block.css('padding-top'),
                    'padding-bottom': block.css('padding-bottom'),
                    visibility : 'none', // невидим
                    display : 'block',   // но занимает место
                });
                document.body.appendChild(block[0]); // переключаемся на body 
                // абсолютим блок, ставим поверх всех и устанавливаем его новые координаты/позициию
                block.css({
                    position: 'absolute',
                    'z-index': 100,
                    left: oldLeft,
                    top: oldTop,
                    width:  oldWidth, 
                    height: oldHeight, 
                });
            };            
            absoluting(); // do it

            // это я делал для того, чтобы во время 
            // трансформации линк collapse/expand не работал
            var resizing = false; 
            var collapse = function() {     
                // выходим, если мы уже ресайзимся или развернуты
                if (collapsed || resizing) { 
                    return;
                }       
                                       
                resizing = true; // поехали
                resizeLink.text('expand ' + title); // меняем тайтл линка
                // изменяем размерчики/позицию блока
                block.css({
                    left: getLeftCollapsed(),
                    width:  oldWidth, 
                    height: oldHeight, 
                });
                resizeIframe(collapsedWidth-deltaWidth, collapsedHeight-deltaHeight); // ресайзим ифрейм
                // закончили
                resizing = false;
                collapsed = true;
            };

            var expand = function() {
                // выходим, если мы уже ресайзимся или свернуты
                if (!collapsed || resizing) {
                    return;
                }       
           
                resizing = true; // поехали    
                resizeLink.text('collapse ' + title); // меняем тайтл линка     
                // изменяем размерчики/позицию блока                
                block.css({
                    left: getLeftRestored(),
                    width: expandWidth, 
                    height: expandHeight, 
                });
                // ресайзим ифрейм/ magic numbers - насколько у меня блок больше по размерам от iframe что в нем 
                resizeIframe(expandWidth-deltaWidth, expandHeight-deltaHeight); 
                // закончили
                resizing = false;
                collapsed = false; 
            };

            // а это модуль ремайза онка браузера
            // и когда пройдет ресайз
            $(window).resize(function() {
                // пересчитаем позицию дива, хорошо что только по left :) 
                block.css({
                    left: (collapsed)?getLeftCollapsed():getLeftRestored(),
                });     
            });
        });
    }
}

Может кому-то пригодится....