Как заставить образец заполнения Рафаэля двигаться с элементом? (положение фонового изображения относительно элемента)

Как я могу дать элементу Рафаэля заливку, которая перемещается при перемещении элемента, например, как CSS background-image из position: absolute; Элемент HTML будет сохранять ту же позицию относительно его местоположения, как он был перемещен?

Вот пример демонстрации: как я могу заставить шаблон фонового изображения элемента Raphael (треугольный путь) вести себя так же, как перетаскиваемый как элемент HTML (квадратный div)?

http://jsbin.com/oxuyeq/8/edit


Этот вопрос, по сути, противоположен тому, как сделать шаблон "фиксированным" в Raphael.js / IE? с симулятором поляризованных очков - я хочу, чтобы поведение, специфичное для IE, которое они пытались избежать, происходило последовательно во всех браузерах (включая IE8).

Как подробно описано в этом другом вопросе, только в IE8 (VML) элемент Raphael ведет себя так, как я хочу; но даже это ошибочно: разные вещи, как звонки setSize на paper элемент или переопределение заливки (по существу, все, что вызывает перерисовку), заставляет его переключаться на другое поведение.


Есть вопрос, похожий на этот вопрос для чистого SVG, но без каких-либо хороших ответов на момент написания, и, конечно, ни один из них не подходит для Рафаэля.


Редактировать 2: Наблюдая за тем, что происходит в режиме SVG, кажется, что каждое преобразование Рафаэля также автоматически выполняет то же преобразование матрицы в SVG <pattern> элемент. Я думаю, что это то, что вызывает поведение, которого я пытаюсь избежать - я думаю, patternContentUnits а также patternUnits не связаны. Здесь есть нерешенная проблема, которая кажется связанной здесь (выделение той же проблемы с помощью clip-rect, рядом со строкой this.pattern && updatePosition(this);) - https://github.com/DmitryBaranovskiy/raphael/issues/638

Поэтому одной из возможностей может быть определение пользовательского атрибута, который применяет преобразование к элементу, не применяя его также к шаблону. Звучит сложно - может потребоваться взлом Raphael или дублирование большого количества кода преобразования Raphael. Надеюсь, есть другой способ. И, боже, помоги нам сделать эту работу в VML...


Редактировать 3: Некоторая потенциально важная информация, эта проблема возникает не только при заполнении пути. Рафаэль image элементы, созданные с paper.image() У меня нет этой проблемы в режиме SVG, но есть очень похожая проблема, которая действительно плоха в режиме IE8 VML. Вот демонстрация нескольких способов заставить элементы изображения двигаться, и вот сравнение, показывающее, как все они работают без ошибок в IE, и все терпят неудачу в IE:

введите описание изображения здесь

2 ответа

Я недавно наткнулся на похожую проблему. Я нигде не мог найти рабочее решение, поэтому я нашел свое собственное. Я использовал ответ @user568458 в качестве отправной точки. Сначала я изменил функцию updatePosition так:

 updatePosition = function (o) {
    if(!o.data("relativeFill")) { //data is a custom store for user's properties
        var bbox = o.getBBox(1);
        $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
    }
},

Я также изменил fill в случае если setFillAndStroke функционировать так:

var relativeFill = o.data("relativeFill"),
isURL = Str(value).match(R._ISURL);
if (isURL) {
    el = $("pattern");
    var ig = $("image");
    el.id = R.createUUID();
    $(el, {x: 0, y: 0, patternUnits: relativeFill ? "objectBoundingBox" : "userSpaceOnUse", height: 1, width: 1});
    $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
    el.appendChild(ig);

    (function (el) {
        R._preload(isURL[1], function () {
            var w = this.offsetWidth,
                h = this.offsetHeight,
                bbox = o.getBBox();
            $(el, {width: 1, height: 1});
            $(ig, {width: relativeFill ? bbox.width : w, height: relativeFill ? bbox.height : h});
            o.paper.safari();
        });
    })(el);
    o.paper.defs.appendChild(el);
    $(node, {fill: "url(#" + el.id + ")"});
    o.pattern = el;
    o.pattern && updatePosition(o);
    break;
}

Тогда всякий раз, когда вы хотите использовать его, вы должны установить yourElement.data({ relativeFill: true })

Вот рабочий пример с относительной и многократной заливкой: https://codepen.io/mmazur/pen/Gmebrj?editors=0010


Я согласен с @user568458, это решение очень уродливо и может перестать работать с будущим обновлением Raphael. Тем не менее, Рафаэль не сильно изменился в последнее время, поэтому я согласен с этим риском.


РЕДАКТИРОВАТЬ: Исправлена ​​ошибка, описанная здесь: изображение размыто при использовании шаблона заполнения URL для круга SVG

Редактировать:

Для этого есть грубый обходной путь, который не требует взлома кода ядра Raphael, но зависит от не совсем стабильной ошибки IE, описанной https://github.com/DmitryBaranovskiy/raphael/issues/177

Это работает, скрывая объект образца SVG от Рафаэля, переименовывая его в объект Рафаэля, вызывая updatePosition() не быть призванным. Это останавливает Рафаэля, перемещающего фоновое изображение в режиме SVG. В режиме VML в IE8 есть ошибка, из-за которой, хотя код перемещает <fill> элемент, это не обновляется на экране, пока не будет проведена перерисовка.

path.patternTmp = path.pattern;
delete path.pattern;

Это имеет тот же недостаток, что и предложенное ниже, что в режиме VML это связано с ошибкой IE. Но это работает для простых случаев. Я не проверил достаточно полно, чтобы увидеть, есть ли другие побочные эффекты этого исправления, но не должно быть - посмотрел на исходный код Рафаэля, похоже, updatePosition() это единственная функция Рафаэля, которая использует element.pattern,

http://jsbin.com/oxuyeq/10/edit

Обратите внимание, что path.pattern нужно переименовывать так каждый раз, когда применяется заливка изображения - поэтому, если вы по какой-либо причине повторно применяете заливку, вам нужно повторно сделать это исправление - и это повторное применение заливки "исправит" ошибку IE, от которой мы зависим в результате чего изображение не синхронизируется с объектом.


Оригинальный ответ: вот что-то вроде исправления. Это уродливо двумя способами:

  • Для SVG он основан на взломе кода ядра Raphael (но только небольшой взлом)
  • Для VML он основан на ошибке перерисовки Internet Explorer и завершится ошибкой, если ваш код вызывает перерисовку

Но вы можете получить относительные позиции, если ваш код не вызывает перерисовку IE (например, путем переустановки заполнения элемента).

В идеале, был бы способ сделать это, который работает надежно в IE VML, а не полагаться на ошибку.


Вы можете сделать это безобразно почти исправить, отключив функцию Рафаэля updatePosition() как угодно. Описанное выше поведение не является SVG-значением по умолчанию, это функция Raphael, определенная в updatePosition() функция, которая, кажется, разработана для того, чтобы поведение элементов SVG Raphael соответствовало поведению элементов VML , если бы Internet Explorer не был набором ошибок, содержащихся вместе с ошибками. Обратите внимание, что updatePosition() функция вызывается только в режиме SVG - по умолчанию предполагается, что заливки VML ведут себя так (но не надежно из-за ошибки перерисовки, описанной в приведенном выше связанном вопросе...).

Мой (безобразный) способ сделать это - изменить updatePosition() код функции к этому:

updatePosition = function (o) {
    if(!o.relativeFill) {  // <<< added this condition
        var bbox = o.getBBox(1);
        $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});          
    }
},

... затем, для каждого элемента, который вы хотите иметь относительную заливку, вы устанавливаете someElement.relativeFill = true;


Обратите внимание, что это не очень хорошая практика - например, в будущем обновлении Raphael может использоваться пространство имен lativeFill для чего-то другого. Это просто пример липкой штукатурки, которая (почти) работает.


число рейнольдса Режим VML: Теоретически вы можете сделать это исправление надежным и не зависящим от ошибок, добавив аналогичное условие к месту в функциях Рафаэля в режиме VML setFillAndStroke и / или setCoords этот набор fill.position для динамического расчета. Кажется, это VML-эквивалент updatePosition(), Теоретически, VML должен по умолчанию установить заливку относительно формы, если alignshape = true для элемента fill, который в теории должен быть истинным по умолчанию.

Но, похоже, это не работает на практике. Из моего тестирования полагаться на ошибку IE, как описано выше, на самом деле кажется более надежным, чем пытаться заставить IE VML работать так, как описано в документации. В моем тестировании довольно сложно заставить IE8 перерисовать заливку: кажется, это делает только сброс заливки.

Другие вопросы по тегам