如何从 clojurescript 中的单个函数 return 两个 customSVGSeries

How to return two customSVGSeries from a single function in clojurescript

我想用一个函数画两个不同的customCVGseries;但是,当然,此代码(条带化到最低限度)仅 returns 最后一个:

(defn make-label-with-line [x y key]     
   ^{:key key}
   [:> rvis/CustomSVGSeries {:onValueMouseOver (fn [] (reset! mouse-over? true))
                             :onValueMouseOut  (fn [] (reset! mouse-over? false))
                             :data [{:x x :y y
                                  :customComponent (fn [_ position-in-pixels]
                                    (if (and @middle-button-pressed? @mouse-over?)
                                      (reset! pos (calculate-xy position-in-pixels)))
                                    (let [[delta-x delta-y] @pos]
                                     (r/as-element [:g 
                                                      [:text
                                                        [:tspan {:x delta-x :y (+ delta-y 18)} "Hidrógeno "]
                                                        [:tspan {:x delta-x :y (+ delta-y 36)} "Alfa"]]])))}]}]
   ^{:key (str key "line")}
   [:> rvis/CustomSVGSeries {:data [{:x x :y y
                                  :customComponent (fn []
                                    (let [[delta-x delta-y] @pos]
                                     (r/as-element [:g 
                                                      [:polyline {:points [0 0 0 delta-y delta-x delta-y]
                                                                  :stroke "black" :fill "none"}]])))}]}])

我尝试将两者都包装在 :div 中,甚至包装在向量中([]),但我得到了错误(如果它们有用,我可以复制它们) .

我需要它们是两个元素,而不是一个,因为我只需要第一个知道 :onValueMouseOver:onValueMouseOut 事件:我需要它们 'drag'图表上的标签 (:text),而 polyline 可能太大并延伸到图表的大部分,并捕获不需要的事件。

在此屏幕截图中,我显示了使用以下工作代码时这些事件捕获的区域:

one customSVGseries is "too" big

(r/as-element [:g
                 [:polyline {:points [0 0 0 inc-y inc-x inc-y]
                            :stroke "black" :fill "none"}]
                 [:text
                    [:tspan {:x delta-x :y (+ delta-y 18)} "Hidrógeno "]
                    [:tspan {:x delta-x :y (+ delta-y 36)} "Alfa"]]])

我什至认为使用两个 line(而不是 polyline)“区域”会更“有限”;我的意思是,用户应该将鼠标 正好 [=7​​6=] 放在 line 上以触发事件。但是我错了:受事件影响的区域是一样的。

(r/as-element [:g
                 [:line {:x1 0 :y1 0 :x2 0 :y2 inc-y :stroke "black"}]
                 [:line {:x1 0 :y1 inc-y :x2 inc-x :y2 inc-y :stroke "black"}]
                 [:text
                    [:tspan {:x delta-x :y (+ delta-y 18)} "Hidrógeno "]
                    [:tspan {:x delta-x :y (+ delta-y 36)} "Alfa"]]])

我正在考虑使用两个函数(一个用于 text,一个用于 polyline);但应该有更好的方法! :) 最让我困扰的是我一定遗漏了一些明显的东西...... :/

编辑

我尝试了 Eugene Pakhomov 提出的解决方案,但现在两个系列都没有出现在图中;我也没有收到任何错误(就好像它们被注释掉了......)。我复制了完整的函数以防遗漏一些明显的东西:

(let [mouse-over? (atom false) 
      pos (atom [0 18])]
  (defn crear-etiqueta [x y key position] 
    (if-not (= position [0 18]) (reset! pos position))
    [:<>
      ^{:key key}
      [:> rvis/CustomSVGSeries {:onValueMouseOver (fn [d] (reset! mouse-over? true))
                                :onValueMouseOut  (fn [d] (if-not @button-cen-pressed? (reset! mouse-over? false)))
                                :data [{:x x :y y
                                  :customComponent (fn [_ position-in-pixels]
                                    (if (and @button-cen-pressed? @mouse-over?)
                                      (reset! pos (calcular-xy-etiqueta position-in-pixels)))
                                    (let [[inc-x inc-y] @pos]
                                     (r/as-element [:g {:className "etiqueta"}
                                                      [:text
                                                        [:tspan {:x inc-x :y (+ inc-y 0)} "Hidrógeno "]
                                                        [:tspan {:x inc-x :y (+ inc-y 18)} "Alfa"]]])))}]}]
      ^{:key (str key "line")}
       [:> rvis/CustomSVGSeries {:data [{:x x :y y
                                  :customComponent (fn []
                                    (let [[inc-x inc-y] @pos]
                                     (r/as-element [:g {:className "etiqueta"}
                                                     [:polyline {:points [0 (if (< inc-y 5) -10 5) 0 inc-y inc-x inc-y]
                                                                 :stroke "black" :fill "none"}]])))}]}]
      ]))

编辑 2

我更糊涂了。阅读有关 []() here 的用法的文章,我这样调用 crear-etiqueta[crear-etiqueta 100 100 "key" [0 0]]... 但那更糟糕!我什至尝试了最简单的情况,但没有成功:

(defn test-component [x y]
  ^{:key key}
      [:> rvis/CustomSVGSeries {:data [{:x x :y y :customComponent "square" :size 30}]}])

(defn line-chart []
  [:div
  [:> rvis/FlexibleXYPlot
[...]
    [test-component 176 550]]])

但如果我将 [test-component 176 550] 更改为 (test-component 176 550),它会起作用。

请原谅我的流浪;我意识到我还在学习。

编辑 3

Eugene Pakhomov 的解决方案当然有效......至少当创建两个元素的函数被称为“简单”时。现在我有另一个问题:

该函数应该在一组项目上调用,每个项目都具有这种形式:

{:etiqueta-1 {:y 6071.758666687525, :x 176.60089063427614, :texto ["176.6"], :pos [0 18], :mouse-over? false}}

所以我试着像这样插入它们:

(into [:> rvis/FlexibleXYPlot {...}] 
      (doall (for [[id {:keys [x y texto]}] (:etiquetas (get @perfiles @perfil-activo))]
               (crear-etiqueta id x y texto [@perfil-activo :etiquetas id])))

但这不起作用。它什么也没显示。我更新了回购以显示这一点。

每当您需要 return 来自单个 Reagent 组件的多个元素时,请使用 React fragments. In Reagent,这意味着将这些多个元素包装在一个 [:<> ...].

但是当涉及到 react-vis 和 React 片段时,你似乎运气不好 - 该库实际上并没有直接呈现子元素(使用 rvis/CustomSVGSeries 创建的元素)而是它从它们中提取所有信息,然后根据这些信息构建它需要的东西。 React 片段本身不是系列,react-vis 不会进入片段。

但是,您可以做的是使 series-creating 函数 return 成为一个简单的序列向量(不需要 :key 元数据),并且 into 创建 rvis/FlexibleXYPlot 元素的 Hiccup 向量中的那个向量:

(into [:> rvis/FlexibleXYPlot {...}]
      (create-vector-of-series))