如何从 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
)“区域”会更“有限”;我的意思是,用户应该将鼠标 正好 [=76=] 放在 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))
我想用一个函数画两个不同的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
)“区域”会更“有限”;我的意思是,用户应该将鼠标 正好 [=76=] 放在 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))