Идеи для рендеринга HTML * в формах Raphael (SVG/VML)

Я работаю над приложением, которое использует Raphael для рисования примитивных фигур (прямоугольников, эллипсов, треугольников и т.д.) и линий, но позволяет пользователь может перемещать/изменять размер этих объектов. Одним из основных требований является то, что лицо форм может иметь форматированный текст. Фактический текст - это подмножество Markdown (простые вещи, такие как жирный шрифт, курсив, списки) и отображается как HTML.

FWIW - Я использую Backbone.js для модуляции логики формы.

Подход 1

Моя первоначальная мысль заключалась в использовании комбинации foreignObject для SVG и прямого HTML с VML для IE. Однако IE9 не поддерживает foreignObject, и поэтому этот подход пришлось отказаться.

Подход 2

Рядом с объектом canvas добавьте div, который содержит фактический HTML. Затем поместите их поверх фактической фигуры с прозрачным фоном. Я создал представление формы, которое имеет ссылки как на фактическую форму Рафаэля, так и на "наложение" div. Существует несколько проблем с этим подходом:

  • Использование оверлей, которые не являются дочерними элементами контейнера SVG/VML, кажется неправильным. Имеет ли этот элемент оверлея другие проблемы при рендеринге по дороге?

  • События, которые обычно захватываются Рафаэлем (перетаскивание, клик и т.д.), необходимо перенаправить из оверлея в объект Рафаэля. Для браузеров, поддерживающих pointer-events, это легко сделать:

    div.shape-text-overlay {
      position: absolute;
      background: none;
      pointer-events: none;
    }
    

    Однако другие браузеры (например, IE8 и ниже) нуждаются в пересылке событий:

    var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout';
    this.$elText.on(forwardedEvents, function(e) {
    
      var original = e.originalEvent;
    
      var event;
      if (document.createEvent) {
        event = document.createEvent('HTMLEvents');
        event.initEvent(e.type, true, true);
      } 
      else {
        event = document.createEventObject();
        event.eventType = e.type;
      }
    
      // FYI - This is the most simplistic approach to event forwarding. 
      // My real implementation is much larger and uses MouseEvents and UIEvents separately.
    
      event.eventName = e.type;
      _.extend(event, original);
    
      if (document.createEvent) {
        that.el.node.dispatchEvent(event);
      } 
      else {
        that.el.node.fireEvent('on' + event.eventType, event);
      }
    
    });
    
  • Перекрывающиеся фигуры заставляют текст перекрываться, потому что текст/фигуры находятся на разных уровнях. Хотя перекрывающиеся формы не будут распространены, это выглядит плохо:

    overlap

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

Подход 3

Почти как Подход 1, но это касается непосредственного ввода текстовых узлов/узлов VML. Самая большая проблема с этим - это необходимое количество ручного преобразования. Нарушение API Raphael кажется, что это может вызвать проблемы стабильности с другими объектами Рафаэля.

Вопрос

Кто-нибудь еще должен был сделать что-то подобное (визуализация HTML внутри SVG/VML)? Если да, то как вы решили эту проблему? Учитывались ли события?

Ответы

Ответ 1

Если другой ответ не может быть предоставлен и превзойти мое решение, я продолжил дополнительный слой div. Пересылка событий была самой трудной частью (если кому-то требуется код пересылки событий, я могу опубликовать его здесь). Опять же, самый большой недостаток этого решения заключается в том, что перекрывающиеся фигуры заставят их текст перекрываться над фактической фигурой.

Ответ 2

Я построил этот проект (больше не живет), используя Raphael. Я действительно отказался от идеи использования HTML внутри SVG, потому что это было слишком грязно. Вместо этого я абсолютно позиционировал слой HTML поверх слоя SVG и перемещал их вместе. Когда мне захотелось показать HTML, я просто угаснул его и угасал соответствующий объект SVG. Если он рассчитан правильно и правильно выстроен, это не заметно для неподготовленного глаза.

Хотя это может и не быть тем, что вы ищете, возможно, это заставит ваш разум думать о новых способах решения вашей проблемы! Не стесняйтесь смотреть на JS на этой странице, так как она не была анонимной;)

PS, проект является приложением для сбора свинца. Если вы просто хотите посмотреть, как это работает, выберите "Друг" в первом выпадающем меню, и вам не нужно предоставлять какую-либо информацию.