在 clojurescript/试剂中跟踪鼠标并在屏幕上渲染点?

Tracking mouse and render dot at the screen in clojurescript / reagent?

我对 clojurescript 很陌生,也许这是一个微不足道的问题,但我还没有设法找到答案。

我期待实现一个 clojurescript 来跟踪鼠标并在鼠标位置渲染一个点,如下所示:

https://jsbin.com/gejuz/1/edit?html,output

Js代码:

function() {
  "use strict";

  document.onmousemove = handleMouseMove;
  function handleMouseMove(event) {
  var dot, eventDoc, doc, body, pageX, pageY;

  event = event || window.event; // IE-ism

  // If pageX/Y aren't available and clientX/Y
  // are, calculate pageX/Y - logic taken from jQuery
        // Calculate pageX/Y if missing and clientX/Y available
  if (event.pageX == null && event.clientX != null) {
    eventDoc = (event.target && event.target.ownerDocument) || document;
    doc = eventDoc.documentElement;
    body = eventDoc.body;

    event.pageX = event.clientX +
      (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
      (doc && doc.clientLeft || body && body.clientLeft || 0);
    event.pageY = event.clientY +
      (doc && doc.scrollTop  || body && body.scrollTop  || 0) -
      (doc && doc.clientTop  || body && body.clientTop  || 0 );
  }

  // Add a dot to follow the cursor
  dot = document.createElement('div');
  dot.className = "dot";
  dot.style.left = event.pageX + "px";
  dot.style.top = event.pageY + "px";
  document.body.appendChild(dot);
}

到目前为止,我确实设法获得了鼠标坐标(感谢这个问题Tracking mouse in clojurescript / reagent / reagi?)。但我无法在网页中呈现该点。

Clojurescript 代码:

(def mouse-coordinates (reagent/atom {:x 100 :y 100}))

(defn dot [x y]
  [:div {:style {:left (str x "px")
                 :top (str y "px")
                 :width "2px"
                 :height "2px"
                 :background-clor "black"
                 :position "absolute"}}])

(def do-dot (reagent/reactify-component dot))

(defn mouse-move []
  [:body
   {:onMouseMove (fn [event]
                   (swap! mouse-coordinates assoc :x (.-clientX event))
                   (swap! mouse-coordinates assoc :y (.-clientY event))
                   (reagent/create-element do-dot
                                           #js{:x (int (:x @mouse-coordinates))
                                               :y (int (:y @mouse-coordinates))})
                   )}
   [:p "x: " (int (:x @mouse-coordinates))]
   [:p "y: " (int (:y @mouse-coordinates))]
   ])


(reagent/render-component [mouse-move]
                          (. js/document (getElementById "app")))

感谢任何帮助。提前谢谢你。

您可以将 dot 组件作为呈现代码的一部分,而不是在 onMouseMove 事件中创建元素。它将获取 reagent/atom 的更改,就像两个 p 元素所做的那样:

   [:p "x: " (int (:x @mouse-coordinates))]
   [:p "y: " (int (:y @mouse-coordinates))]
   [dot (int (:x @mouse-coordinates)) (int (:y @mouse-coordinates))]

还有一个错字::background-clor -> :background-color。这两个变化应该足以使圆点出现。


还有其他一些有助于简化代码的东西:

如果您传入数字,

style 属性将默认为像素

所以dot组件可以这样写:

(defn dot [x y]
  [:div {:style {:left             x
                 :top              y
                 :width            2
                 :height           2
                 :background-color "black"
                 :position         "absolute"}}])

reset! 对比 swap!

  • 因为 mouse-coordinates 有一个非常特殊的用途,所以在 onMouseMove 事件中使用 reset! 而不是 swap! 会更整洁一些:
(reset! mouse-coordinates {:x (.-clientX event) :y (.-clientY event)})

将组件道具作为地图传入

(defn dot [{:keys [x y]}]
  ...)

[dot @mouse-coordinates]

最终代码看起来像这样:

(def mouse-coordinates (reagent/atom {:x 100 :y 100}))

(defn dot [{:keys [x y]}]
  [:div {:style {:left             x
                 :top              y
                 :width            2
                 :height           2
                 :background-color "black"
                 :position         "absolute"}}])

(defn mouse-move []
  [:body
   {:onMouseMove (fn [event]
                   (reset! mouse-coordinates {:x (.-clientX event) :y (.-clientY event)}))}
   [:p "x: " (:x @mouse-coordinates)]
   [:p "y: " (:y @mouse-coordinates)]
   [dot @mouse-coordinates]])

更新:当我第一次回答这个问题时,我没有意识到每个点都应该是持久的。这是关于如何实现这一点的更新代码(带有注释):

使用坐标集合

(def mouse-coordinates (r/atom []))

(defn dot [{:keys [x y]}]
  [:div {:style {:left             x
                 :top              y
                 :width            2
                 :height           2
                 :background-color "black"
                 :position         "absolute"}}])

(defn mouse-move []
  [:div
   {:onMouseMove (fn [event]
                   (let [x (.-clientX event)
                         y (.-clientY event)
                         ;; If there's already a dot in an identical location, don't add it. This saves unnecessary work and
                         ;; means we can use [x y] as our unique key for our collection.
                         coords-already-exist? (not (empty? (filter #(and (= (:x %) x) (= (:y %) y)) @mouse-coordinates)))]
                     (when-not coords-already-exist?
                       ;; conj the new coordinate to the collection.
                       (swap! mouse-coordinates #(conj % {:x (.-clientX event) :y (.-clientY event)})))))}
   [:p "x: " (:x @mouse-coordinates)]
   [:p "y: " (:y @mouse-coordinates)]
   ;; Loop through the coordinates.
   (for [{:keys [x y]} @mouse-coordinates]
     [dot
      ;; Important: we give each dot a unique key.
      {:key [x y]
       :x   x
       :y   y}])])

如评论中所述,呈现集合的重要之处在于为每个项目赋予唯一键。这意味着随着新坐标的创建,React 知道添加一个新的子节点而不是重新渲染每个 dot。更多信息可以在 React 文档中找到:https://reactjs.org/docs/lists-and-keys.html#keys