Страницы

Поиск по вопросам

Показаны сообщения с ярлыком blur. Показать все сообщения
Показаны сообщения с ярлыком blur. Показать все сообщения

среда, 26 февраля 2020 г.

Можно ли создать в canvas blur эффект на фото при mousemove?

#javascript #html5 #canvas #blur


Можно ли создать на canvas библиотеке Konva.js (или на каком-то другом) blur эффект
на фото при mousemove?

Читал документацию к Konva js, но ничего подходящего не нашел. Искал примеры, достойных
примеров не нашел.

Вот пример с встроенным фильтром в canvas ctx.filter = 'blur(5px)';.
    


Ответы

Ответ 1



Конечно можно было бы взять библиотеку, но и вручную можно добится неплохих результатов, а заодно и познакомится поближе с обработкой изображений. Это решение использунт прямые манипуляции с пикселями. Применяем ко всем пикселям области, которую необходимо заблюрить, так называемое "ядро свертки" (convolution kernel) - основа основ в обработке изображений. Вот тут подробно про это написано а так же визуализировано Запустите сниппет заново, если картинка не показательная (они псевдослучайные), в сниппете реализовано рисование блюром, как Вы просили в комментариях... let s = 44; // size of area to be blurred let kernel =[[0,0,1,0,0], [0,1,2,1,0], [1,2,3,2,1], [0,1,2,1,0], [0,0,1,0,0]]; let ctx = canvas.getContext('2d'); let total = kernel.flatMap(i => i).reduce((a, i) => a + Math.abs(i)), ks = (kernel.length-1)/2, // half of kernel size - 1 (pixels around center) s2 = s/2, // half size of area to be blurred sks2 = s+ks*2; // size of area of pixels needed to be processed img.onload = e => ctx.drawImage(img, 0, 0); addEventListener('mousedown', e => draw = true) addEventListener('mouseup', e => draw = false) addEventListener('mousemove', e => { if (!window.draw) return; var input = ctx.getImageData(e.layerX-s2-ks, e.layerY-s2-ks, sks2, sks2); var output = ctx.createImageData(s, s); for (var x = 0; x < s; x++) for (var y = 0; y < s; y++) handlePixel(input, output, x, y) ctx.putImageData(output, e.layerX - s2, e.layerY - s2) }) function handlePixel(i, o, x, y) { let offset = (y * s + x) * 4; // offset in output data (4=rgba) for (var kx = -ks; kx <= ks; kx++) for (var ky = -ks; ky <= ks; ky++) { let off = ((y+ks) * sks2 + (x+ks) + kx + ky * sks2) * 4; //offset in input for (var n = 0; n < 3; n++) o.data[offset + n] += kernel[kx+ks][ky+ks] * i.data[off+n] / total; } o.data[offset + 3] = 255; // always opaque } PS: от бага по краям надо подумать как избавиться...

среда, 5 февраля 2020 г.

Мигает картинка при использовании blur

#html #css #анимация #blur


При наведении курсора на картинку, добавляется с помощью JS class .active

В стиле active  указано, что картинка должно стать blur(10px) и transform: translateY(20px); 

Анимация active выполняется отлично, но если мы захотим убрать active, чтобы вернуть
обычное состояние, то картинка мигает перед концом анимации...

Как можно исправить?
    


Ответы

Ответ 1



Сниппет для воспроизведения проблемы: for (let img of document.querySelectorAll('.test')) { img.addEventListener('mouseenter', function () { this.classList.add('active'); }); img.addEventListener('mouseleave', function () { this.classList.remove('active'); }); } .test { transition: all 0.3s ease-out; } .active { filter: blur(10px); transform: translateY(20px); } Причина - ресурсоемкость размытия. Соответственно, на 100% решить проблему невозможно. Но, можно применить CSS-вуду некоторые оптимизации, которые помогут в большинстве случаев. upd.: После прочтения комментариев и проверки в Firefox (Win, Andr) и Opera (Win), делаю вывод что я ошибался относительно причин. Видимо, они все-таки в Chrome под винду и андроид. Вариант решения: for (let img of document.querySelectorAll('.test')) { img.addEventListener('mouseenter', function () { this.classList.add('active'); }); img.addEventListener('mouseleave', function () { this.classList.remove('active'); }); } .test { transition: filter 0.3s steps(18), transform 0.3s ease-out; backface-visibility: hidden !important; transform: translate3d(0, 0, 0) scale(1, 1); } .active { filter: blur(10px); transform: translate3d(0, 20px, 0) scale(1, 1); } Данное решение включает в себя (в порядке значимости): ограничение числа кадров перехода, steps(18) 60кадров/с (лимит браузера) * 0,3с = 18 Это также делает временную функцию перехода линейной, что сильно ускоряет подсчеты (но это не просто аналог linear, а лучше). отключение отрисовки оборотной стороны элемента через backface-visibility: hidden Смысл очевиден: флип не выполняется => нет смысла рендерить оборотку. исключение из пересчета transform трансформации по X и Z, а также масштабирования Плюс, translate3d создает новый слой при рендеринге элемента - что, в свою очередь, должно вынудить браузер задействовать GPU не только при размытии.

пятница, 24 января 2020 г.

Как в обработчике blur дождаться окончания обработки в focus

#jquery #angularjs #blur #focus


Возникла проблема с созданием datepicker для поля даты. На поле висит 2 обработчика
- blur и focus, в focus создается datepicker, в blur он удаляется. Причем в focus он
создается с помощью deferred объекта.

.directive('datePicker', [ '$compile', '$document', 'datePickerService', '$templateRequest',
function ($compile, $document, datePickerService, $templateRequest) { 
    function link(scope, element, attrs) {
            var picker = null;
                var $elementFocusProcessing = $.Deferred();
                element.on('focus', function (event) {
                    populatelimits();
                    prepareViewData();
                    var pos = angular.extend(element.offset(), { height: element[0].offsetHeight
});                    

                    $templateRequest(datePickerService.datePickerTemplate).then(function
(html) {
                        var template = angular.element(html);
                        $document.find('body').append(template);
                        picker = $compile(template)(scope);
                        picker.css({ top: pos.top + pos.height + 5, left: pos.left,
display: 'block', position: 'absolute' });

                        picker.on('mousedown', function (evt) {
                            evt.preventDefault();
                        });
                        $elementFocusProcessing.resolve();
                    });
                });

                element.on('blur', function () {
                    $elementFocusProcessing.then(function () {
                        if (picker) {
                            picker.remove();
                            picker = null;
                        };
                        $elementFocusProcessing = $.Deferred();
                    });
                });
            }

        return {
            restrict: 'A',
            link: link,
            scope : {
                model: '=ngModel'
            }
        };


И собственно проблема: если выделить поле даты, потом переключиться на другое окно,
а затем кликнуть на любое место страницы, кроме поля даты, возникают подряд несколько
событий: focus поля, blur поля, click по странице. При этом создание picker в focus
завершается позже, чем попытка его удаления в blur, и он не удаляется, хотя фокус элемента
в тоге оказывается потерян. Как сделать так, чтобы выполнение кода в blur происходило
только после завершения выполнения кода в focus? Мне пока пришло в голову только сделать
дополнительную переменную focusEnded для определения того, идет ли выполнение кода
в focus, а в blur организовать цикл с таймаутом, но чувствую, что можно более изящно
сделать это с использованием deferred объектов.
    


Ответы

Ответ 1



Так как рабочего примера нет, можно рассуждать только теоретически: Так как шаблон не меняется, его не нужно подгружать не только на каждый фокус, но и на каждый link. Следовательно, его можно вынести из функции и результат, а он является promise, сохранить в переменной: var templatePromise = $templateRequest(datePickerService.datePickerTemplate); Далее, функция link, внутри нее подразумеваем, что шаблон уже подгрузился, для этого все остальное делаем в обработчике success функции then у нашего сохраненного templatePromise function link(scope,element,attrs){ templatePromise.then(function(html){ //достаточно сделать всего один элемент, // который мы будем добавлять или удалять var template = angular.element(html); //и добавим событие, чтобы не добавлять его каждый раз //так как template это уже объект jqLite можно сделать так template.on('mousedown', function (evt) { evt.preventDefault(); } ); //добавляем on focus element.on('focus', function (event) { ... $document.find('body').append(template); $compile(template)(scope); //так как template это объект jqLite, то вместо picker можно использовать его template.css(...) ... }); //добавляем on blur element.on('blur', function () { //так как добавляли template, то и удалять можно его //а так как он у нас всегда есть, то не надо проверять на null template.remove(); }); }); }

вторник, 16 апреля 2019 г.

Мигает картинка при использовании blur

При наведении курсора на картинку, добавляется с помощью JS class .active
В стиле active указано, что картинка должно стать blur(10px) и transform: translateY(20px);
Анимация active выполняется отлично, но если мы захотим убрать active, чтобы вернуть обычное состояние, то картинка мигает перед концом анимации...
Как можно исправить?


Ответ

Сниппет для воспроизведения проблемы:
for (let img of document.querySelectorAll('.test')) { img.addEventListener('mouseenter', function () { this.classList.add('active'); }); img.addEventListener('mouseleave', function () { this.classList.remove('active'); }); } .test { transition: all 0.3s ease-out; } .active { filter: blur(10px); transform: translateY(20px); }
Причина - ресурсоемкость размытия. Соответственно, на 100% решить проблему невозможно. Но, можно применить CSS-вуду некоторые оптимизации, которые помогут в большинстве случаев. upd.: После прочтения комментариев и проверки в Firefox (Win, Andr) и Opera (Win), делаю вывод что я ошибался относительно причин. Видимо, они все-таки в Chrome под винду и андроид.

Вариант решения:
for (let img of document.querySelectorAll('.test')) { img.addEventListener('mouseenter', function () { this.classList.add('active'); }); img.addEventListener('mouseleave', function () { this.classList.remove('active'); }); } .test { transition: filter 0.3s steps(18), transform 0.3s ease-out; backface-visibility: hidden !important; transform: translate3d(0, 0, 0) scale(1, 1); } .active { filter: blur(10px); transform: translate3d(0, 20px, 0) scale(1, 1); }
Данное решение включает в себя (в порядке значимости):
ограничение числа кадров перехода, steps(18) 60кадров/с (лимит браузера) * 0,3с = 18 Это также делает временную функцию перехода линейной, что сильно ускоряет подсчеты (но это не просто аналог linear, а лучше). отключение отрисовки оборотной стороны элемента через backface-visibility: hidden Смысл очевиден: флип не выполняется => нет смысла рендерить оборотку. исключение из пересчета transform трансформации по X и Z, а также масштабирования Плюс, translate3d создает новый слой при рендеринге элемента - что, в свою очередь, должно вынудить браузер задействовать GPU не только при размытии.

вторник, 19 марта 2019 г.

Как в обработчике blur дождаться окончания обработки в focus

Возникла проблема с созданием datepicker для поля даты. На поле висит 2 обработчика - blur и focus, в focus создается datepicker, в blur он удаляется. Причем в focus он создается с помощью deferred объекта.
.directive('datePicker', [ '$compile', '$document', 'datePickerService', '$templateRequest', function ($compile, $document, datePickerService, $templateRequest) { function link(scope, element, attrs) { var picker = null; var $elementFocusProcessing = $.Deferred(); element.on('focus', function (event) { populatelimits(); prepareViewData(); var pos = angular.extend(element.offset(), { height: element[0].offsetHeight });
$templateRequest(datePickerService.datePickerTemplate).then(function (html) { var template = angular.element(html); $document.find('body').append(template); picker = $compile(template)(scope); picker.css({ top: pos.top + pos.height + 5, left: pos.left, display: 'block', position: 'absolute' });
picker.on('mousedown', function (evt) { evt.preventDefault(); }); $elementFocusProcessing.resolve(); }); });
element.on('blur', function () { $elementFocusProcessing.then(function () { if (picker) { picker.remove(); picker = null; }; $elementFocusProcessing = $.Deferred(); }); }); }
return { restrict: 'A', link: link, scope : { model: '=ngModel' } };
И собственно проблема: если выделить поле даты, потом переключиться на другое окно, а затем кликнуть на любое место страницы, кроме поля даты, возникают подряд несколько событий: focus поля, blur поля, click по странице. При этом создание picker в focus завершается позже, чем попытка его удаления в blur, и он не удаляется, хотя фокус элемента в тоге оказывается потерян. Как сделать так, чтобы выполнение кода в blur происходило только после завершения выполнения кода в focus? Мне пока пришло в голову только сделать дополнительную переменную focusEnded для определения того, идет ли выполнение кода в focus, а в blur организовать цикл с таймаутом, но чувствую, что можно более изящно сделать это с использованием deferred объектов.


Ответ

Так как рабочего примера нет, можно рассуждать только теоретически:
Так как шаблон не меняется, его не нужно подгружать не только на каждый фокус, но и на каждый link. Следовательно, его можно вынести из функции и результат, а он является promise, сохранить в переменной:
var templatePromise = $templateRequest(datePickerService.datePickerTemplate); Далее, функция link, внутри нее подразумеваем, что шаблон уже подгрузился, для этого все остальное делаем в обработчике success функции then у нашего сохраненного templatePromise
function link(scope,element,attrs){ templatePromise.then(function(html){ //достаточно сделать всего один элемент, // который мы будем добавлять или удалять var template = angular.element(html);
//и добавим событие, чтобы не добавлять его каждый раз //так как template это уже объект jqLite можно сделать так template.on('mousedown', function (evt) { evt.preventDefault(); } );
//добавляем on focus element.on('focus', function (event) { ... $document.find('body').append(template); $compile(template)(scope); //так как template это объект jqLite, то вместо picker можно использовать его template.css(...) ... });
//добавляем on blur element.on('blur', function () { //так как добавляли template, то и удалять можно его //а так как он у нас всегда есть, то не надо проверять на null template.remove(); }); }); }