Создание отражения рисунка с помощью JavaScript
HTML 5 приходит на помощь
Постановка задачи: из исходного изображения

получить отраженное изображение:

Дополнительное условие: генерировать изображение на стороне клиента (то есть без использования GD, ImageMagick и иже с ними).
В черновике стандарта HTML 5 пристутствует такой замечательный элемент как <canvas>. Если вкратце, то данный элемент предназначен для создания изображений при помощи JavaScript.
Впервые элемент <canvas> был представлен компанией Apple и использовался как компонент WebKit для Mac OS X в таких приложениях как Dashboard и Safari.
Поддержка <canvas> в Gecko появилась в версии 1.5, в Presto с версии 9.0 веб-браузера Opera. Текущие версии Internet Explorer (включая восьмую бету) не поддерживают <canvas>. Несмотря на это, для поставленной задачи существует кросс-браузерное решение.
В Microsoft Internet Explorer отражения с прозрачностью можно достичь путём использования комбинации фильтров. В частности, для вертикального отражения используется фильтр flipv, для горизонтального — fliph. Про реализацию прозрачности в Internet Explorer, наверное, знает каждый: progid:DXImageTransform.Microsoft.Alpha. Тем не менее, не все знают, что можно задавать градиентную прозрачность.
Перейдём к решению. Пусть у нас имеется такая разметка:
<img id="image" src="image.png" alt="Image"/>
</div>
Будем полагать, что ширина и высота контейнера заданы (если мы делаем отражение по вертикали, то ширина контейнера совпадает с шириной рисунка, а высота контейнера в два раза больше высоты рисунка; аналогично для горизонтального отражения).
Начнём с классики (MSIE).
* @param container <div id="container">
* @param image <img id="image">
* @param ratio Коэффициент сжатия/растягивания отражения
* @param opacity_start Начальная (ближняя к отражаемому изображению) непрозрачность
* @param opacity_end Конечная непрозрачность
* @param horizontal 0 = вертикальное отражение, 1 = горизонтальное отражение
*/
function reflect_image(container, image, ratio, opacity_start, opacity_end, horizontal)
{
var reflection = document.createElement('img');
reflection.src = image.src;
reflection.style.width = image.width + 'px';
if (0 == horizontal) {
reflection.style.filter = 'flipv progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity_start*100)+', style=1, finishOpacity='+(opacity_end*100)+', startx=0, starty=0, finishx=0, finishy='+(ratio*100)+')';
}
else {
reflection.style.filter = 'fliph progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity_start*100)+', style=1, finishOpacity='+(opacity_end*100)+', startx=0, starty=0, finishx='+(ratio*100)+', finishy=0)';
}
container.appendChild(reflection);
}
В случае с IE всё просто и сводится к заданию соответствующих фильтров. С браузерами, поддерживающими HTML 5, всё гораздо сложнее (ad deliberandum: сколько строк занимает программа на C++, если она использует COM-технологию?)
* @param container <div id="container">
* @param image <img id="image">
* @param ratio Коэффициент сжатия/растягивания отражения
* @param opacity_start Начальная (ближняя к отражаемому изображению) непрозрачность
* @param opacity_end Конечная непрозрачность
* @param horizontal 0 = вертикальное отражение, 1 = горизонтальное отражение
*/
function reflect_image(container, image, ratio, opacity_start, opacity_end, horizontal)
{
var reflection = document.createElement('canvas');
var context = reflection.getContext('2d');
reflection.style.height = image.height + 'px';
reflection.style.width = image.width + 'px';
reflection.height = image.height;
reflection.width = image.width;
//Подводный камень: на canvas нельзя рисовать, если она не в DOM-дереве документа
container.appendChild(reflection);
context.save();
var gradient;
if (0 == horizontal) {
// Задаём точку отсчёта, отражаем ось y и создаём вертикальный градиент
context.translate(0, image.height);
context.scale(1, -1);
gradient = context.createLinearGradient(0, 0, 0, image.height);
}
else {
// Задаём точку отсчёта, отражаем ось x и создаём горизонтальный градиент
context.translate(image.width, 0);
context.scale(-1, 1);
gradient = context.createLinearGradient(0, 0, image.width, 0);
}
// Рисуем исходное изображение (оно будет отражено по одной из осей)
context.drawImage(image, 0, 0, image.width, image.height);
context.restore();
// Задаём режим операции
context.globalCompositeOperation = "destination-out";
// Параметры градинтной заливки (в параметрах передаётся НЕпрозрачность, нам нужна прозрачность)
gradient.addColorStop(1, "rgba(255, 255, 255, " + (1 - opacity_end) + ")");
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity_start) + ")");
context.fillStyle = gradient;
// Заливаем
if (-1 != navigator.appVersion.indexOf('WebKit')) {
context.fill();
}
else {
context.fillRect(0, 0, image.width, image.height);
}
}
Canvas Tutorial в Mozilla Developer Center
Вложения:
Автор: Vladimir; опубликовано в: JavaScript; метки: canvas, HTML, JavaScript, эффектыНоя
2008
Комментарии к статье «Создание отражения рисунка с помощью JavaScript» (9) »
Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.
Оставить комментарий к записи «Создание отражения рисунка с помощью JavaScript»
गते गते पारगते पारसंगते बोधि स्वाहा
Меня зовут Владимир, я программист-фрилансер, специализирующийся на Web-программировании и програмировании под Linux.
По совместительству занимаюсь администрированием LAMP/LNMP-серверов и техническим переводом.


В 90% на заданы. Я не тестил пример, но если критично указание явных размеров, но решение спросом пользоваться не будет
Всё равно всё просто
Я сэкономил на нескольких строчках в JS, чтобы не вносить путаницу.
Для вертикального отражения: ширина контейнера — это ширина рисунка, а высота — две высоты рисунка.
Для горизонтального отражения: ширина контейнера — это две ширины рисунка, а высота — высота рисунка.
Написал на коленке:
* @param image document.getElementById('image')
* @param h_coef Коэффициент растягивания отражения
* @param opacity_start Начальное значение непрозрачности
* @param opacity_end Конечное значение непрозрачности
* @param mode 0 = вертикальное отражение, 1 = горизонтальное
*/
function add_reflection(image, h_coef, opacity_start, opacity_end, mode)
{
var container = document.createElement('div');
var ref_height, ref_width, div_height, div_width;
if (0 == mode) {
ref_height = Math.floor(image.height * h_coef);
ref_width = image.width;
div_height = Math.floor(image.height * (1 + h_coef));
div_width = ref_width;
}
else {
ref_width = Math.floor(image.width * h_coef);
ref_height = image.height;
div_height = ref_height
div_width = Math.floor(image.width * (1 + h_coef));
}
var reflection;
if (!window.opera && document.all) {
reflection = document.createElement('img');
reflection.src = image.src;
reflection.style.width = image.width + 'px';
if (0 == mode) {
reflection.style.filter = 'flipv progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity_start*100)+', style=1, finishOpacity='+(opacity_end*100)+', startx=0, starty=0, finishx=0, finishy='+(h_coef*100)+')';
}
else {
reflection.style.filter = 'fliph progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity_start*100)+', style=1, finishOpacity='+(opacity_end*100)+', startx=0, starty=0, finishx='+(h_coef*100)+', finishy=0)';
}
container.style.width = div_width + 'px';
container.style.height = div_height + 'px';
image.parentNode.replaceChild(container, image);
container.appendChild(image);
container.appendChild(reflection);
}
else {
reflection = document.createElement('canvas');
var context = reflection.getContext('2d');
reflection.style.height = ref_height + 'px';
reflection.style.width = ref_width + 'px';
reflection.height = ref_height;
reflection.width = ref_width;
container.style.width = div_width + 'px';
container.style.height = div_height + 'px';
image.parentNode.replaceChild(container, image);
container.appendChild(image);
container.appendChild(reflection);
context.save();
var gradient;
if (0 == mode) {
context.translate(0, image.height);
context.scale(1, -1);
gradient = context.createLinearGradient(0, 0, 0, ref_height);
}
else {
context.translate(image.width, 0);
context.scale(-1, 1);
gradient = context.createLinearGradient(0, 0, ref_width, 0);
}
context.drawImage(image, 0, 0, image.width, image.height);
context.restore();
context.globalCompositeOperation = "destination-out";
gradient.addColorStop(1, "rgba(255, 255, 255, " + (1 - opacity_end) + ")");
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity_start) + ")");
context.fillStyle = gradient;
if (-1 != navigator.appVersion.indexOf('WebKit')) {
context.fill();
}
else {
context.fillRect(0, 0, image.width, 2*ref_height);
}
}
}
Как пользоваться: HTML:
JavaScript:
В результате скрипт обернет image в
divи задаст ему требуемую высоту и ширину.Да тоже думал когда попробывать реализовать… тока уровня знаний не хватило. А теперь нужда отпала, потому что готово. Спасибо.
Вопрос не втему, но я думаю по адресу, чем отличается clientHeight от offsetHeight
PS на маленьком разрешении (800х600) боковая колонка падает… нехватватет страничке min-width:
Или так: http://www.quirksmode.org/dom/w3c_cssom.html
По-моему, так:
clientHeight=paddingTop+height+paddingBottom
есть стати лекарство которое лечит IE от незнания canavas
http://sourceforge.net/project/showfiles.php?group_id=163391
Привет. Я поработал с твоим кодом, думаю у тебя мелкая ошибка, вместо 2*ref_height должно быть image.height.
не атачится
var children = document.getElementsByTagName('img');
var elements = [];
for (var i = 0; i < children.length; i++) {
var child = children[i];
if(child.getAttribute('alt')=='reflect')
elements.push(child);
}
return elements;
}
function addReflections(){
var rimages = document.myGetElementsByClassName('reflect');
for (i=0;i<rimages.length;i++) {
reflect_image(rimages[i])
}
}
function reflect_image(image)
{
if(!image.complete)
return;
var k = 3; // aka ratio
var opacity_start = 0.31;
var opacity_end = 0.001;
var mode = 1; //0 = vertical, 1 = horizontal
var container = image.parentNode, replace;
if(replace = container.childNodes.length!=1){
container = document.createElement('div');
}
container.style.display='inline-block';
var
ref_height = image.offsetHeight,
ref_width = image.offsetWidth;
var reflection;
if (!window.opera && document.all){
reflection = document.createElement('img');
}else{
reflection = document.createElement('canvas');
var context = reflection.getContext('2d');
}
/* reflection.height = ref_height;
reflection.width = ref_width;*/
if(0 == mode){
reflection.style.display = 'block';
reflection.style.marginTop = '1px';
reflection.style.height = ref_height/k + 'px';
reflection.style.width = ref_width + 'px';
}else{
reflection.style.cssFloat = 'left';
reflection.style.marginRight = '1px';
reflection.style.height = ref_height + 'px';
reflection.style.width = ref_width/k + 'px';
}
/*container.style.width = div_width + 'px';
container.style.height = div_height + 'px';*/
if (!window.opera && document.all) {
reflection.src = image.src;
if (0 == mode) {
reflection.style.filter = 'flipv progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity_start*100)+', style=1, finishOpacity='+(opacity_end*100)+', startx=0, starty=0, finishx=0, finishy='+(100)+')';
}
else {
reflection.style.filter = 'fliph progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity_end*100)+', style=1, finishOpacity='+(opacity_start*100)+', startx=0, starty=0, finishx='+(100)+', finishy=0)';
}
}
else
{
context.save();
var gradient;
reflection.src = image.src;//только для того чтоб знать размеры…
var z1=reflection.width, z2=reflection.height;
if (0 == mode) {
context.translate(0, z2);
context.scale(1, -1);
gradient = context.createLinearGradient(0, 0, 0, ref_width);
}
else {
context.translate(z1, 0);
context.scale(-1, 1);
gradient = context.createLinearGradient(0, 0, ref_height, 0);
}
context.drawImage(image, 0, 0, z1, z2);
context.restore();
context.globalCompositeOperation = "destination-out";
gradient.addColorStop(1, "rgba(255, 255, 255, " + (1 - opacity_start) + ")");
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity_end) + ")");
context.fillStyle = gradient;
if (-1 != navigator.appVersion.indexOf('WebKit')) {
context.fill();
}
else {
context.fillRect(0, 0, z1, z2);
}
}
if(replace){
image.parentNode.replaceChild(container, image);
container.appendChild(image);
}
container.appendChild(reflection);
}
ОХ ТЫ.. СПАСИБО ИСКАЛ НЕЧТО ПОХОЖИЕ…
Хорошая статья.