SVG Öğesi Z Dizinini D3 ile Güncelleme


81

D3 kitaplığını kullanarak bir SVG öğesini z sırasının en üstüne getirmenin etkili bir yolu nedir?

Benim belirli senaryo (a ekleyerek hangi olayları bir pasta grafik strokeiçin pathfare belirli bir parçasının üzerinde olduğunda). Grafiğimi oluşturmak için kod bloğu aşağıdadır:

svg.selectAll("path")
    .data(d)
  .enter().append("path")
    .attr("d", arc)
    .attr("class", "arc")
    .attr("fill", function(d) { return color(d.name); })
    .attr("stroke", "#fff")
    .attr("stroke-width", 0)
    .on("mouseover", function(d) {
        d3.select(this)
            .attr("stroke-width", 2)
            .classed("top", true);
            //.style("z-index", 1);
    })
    .on("mouseout", function(d) {
        d3.select(this)
            .attr("stroke-width", 0)
            .classed("top", false);
            //.style("z-index", -1);
    });

Birkaç seçeneği denedim ama şimdiye kadar şansım olmadı. style("z-index")İkisini classedde kullanmak ve aramak işe yaramadı.

"En üst" sınıf, CSS'mde aşağıdaki gibi tanımlanmıştır:

.top {
    fill: red;
    z-index: 100;
}

fillDeyim ben kapalı doğru / açma biliyordum emin orada yapmaktır. Bu.

Kullanmanın sortbir seçenek olduğunu duydum , ancak "seçili" öğeyi en üste getirmek için nasıl uygulanacağı konusunda emin değilim.

GÜNCELLEME:

Özel durumumu, mouseovervurguyu göstermek için olaydaki SVG'ye yeni bir yay ekleyen aşağıdaki kodla düzelttim .

svg.selectAll("path")
    .data(d)
  .enter().append("path")
    .attr("d", arc)
    .attr("class", "arc")
    .style("fill", function(d) { return color(d.name); })
    .style("stroke", "#fff")
    .style("stroke-width", 0)
    .on("mouseover", function(d) {
        svg.append("path")
          .attr("d", d3.select(this).attr("d"))
          .attr("id", "arcSelection")
          .style("fill", "none")
          .style("stroke", "#fff")
          .style("stroke-width", 2);
    })
    .on("mouseout", function(d) {
        d3.select("#arcSelection").remove();
    });

Yanıtlar:


52

Geliştirici tarafından sunulan çözümlerden biri: "öğeleri yeniden sıralamak için D3'ün sıralama operatörünü kullanın." (bkz. https://github.com/mbostock/d3/issues/252 )

Bu ışığında, verileri karşılaştırarak öğeleri veya verisiz öğelerse konumlarını sıralayabiliriz:

.on("mouseover", function(d) {
    svg.selectAll("path").sort(function (a, b) { // select the parent and sort the path's
      if (a.id != d.id) return -1;               // a is not the hovered element, send "a" to the back
      else return 1;                             // a is the hovered element, bring "a" to the front
  });
})

Bu sadece Z düzeninde işe yaramaz, aynı zamanda yükselen elemanla yeni başlayan herhangi bir sürüklemeyi de bozmaz.
Oliver Bock

3
benim için de çalışmıyor. a, b sadece veri gibi görünüyor, d3 seçim öğesi veya DOM öğesi değil
nkint

Nitekim, @nkint için olduğu gibi a.idve karşılaştırılırken işe yaramadı d.id. Çözüm, doğrudan karşılaştırmak ave d.
Barış Bol Yapar

Bu, çok sayıda alt öğe için yeterince çalışmayacaktır. Yükseltilmiş öğeyi daha sonra yeniden oluşturmak daha iyidir <g>.
tribalvibes

1
Alternatif olarak, svg:useeleman dinamik olarak yükseltilmiş elemana işaret edebilir. Çözüm için stackoverflow.com/a/6289809/292085 adresine bakın .
tribalvibes

91

Diğer yanıtlarda açıklandığı gibi, SVG'nin bir z-endeksi kavramı yoktur. Bunun yerine, belgedeki öğelerin sırası çizimdeki sırayı belirler.

Öğeleri manuel olarak yeniden sıralamanın dışında, belirli durumlar için başka bir yol vardır:

D3 ile çalışırken, genellikle her zaman diğer öğe türlerinin üzerine çizilmesi gereken belirli öğe türlerine sahip olursunuz .

Örneğin, grafikleri düzenlerken, bağlantılar her zaman düğümlerin altına yerleştirilmelidir. Daha genel olarak, bazı arka plan öğelerinin genellikle diğer her şeyin altına yerleştirilmesi gerekirken, bazı vurgular ve kaplamaların yukarıya yerleştirilmesi gerekir.

Bu tür bir durumla karşılaşırsanız, bu öğe grupları için ana grup öğeleri oluşturmanın en iyi yol olduğunu buldum . SVG'de gbunun için öğesini kullanabilirsiniz . Örneğin, her zaman düğümlerin altına yerleştirilmesi gereken bağlantılarınız varsa, aşağıdakileri yapın:

svg.append("g").attr("id", "links")
svg.append("g").attr("id", "nodes")

Şimdi, bağlantılarınızı ve düğümlerinizi boyadığınızda, aşağıdaki gibi seçin ( #öğe kimliğine referansla başlayan seçiciler ):

svg.select("#links").selectAll(".link")
// add data, attach elements and so on

svg.select("#nodes").selectAll(".node")
// add data, attach elements and so on

Şimdi, tüm bağlantılar her zaman tüm düğüm öğelerinden önce yapısal olarak eklenecektir. Böylelikle SVG, öğeleri ne sıklıkta ve hangi sırada ekleyip çıkardığınız önemli olmaksızın tüm düğümlerin altındaki tüm bağlantıları gösterecektir. Elbette, aynı türdeki (yani aynı kap içindeki) tüm öğeler, eklendikleri sıraya tabi olacaktır.


1
Asıl sorunum öğelerimin gruplara ayrılması olduğunda, kafamda bir öğeyi yukarı taşıyabileceğim fikriyle karşılaştım. "Gerçek" probleminiz tek tek öğeleri hareket ettirmekse, önceki cevapta açıklanan sıralama yöntemi iyi bir yoldur.
John David Five

Harika! Teşekkür ederim notan3xit, günümü kurtardın! Yalnızca başkaları için bir fikir eklemek için - istenen görünürlük sırasından farklı sırayla öğeler eklemeniz gerekiyorsa, yalnızca uygun görünürlük sırasına göre gruplar oluşturabilir (bunlara herhangi bir öğe eklemeden önce bile) ve sonra bunlara öğeler ekleyebilirsiniz. herhangi bir sırayla gruplar.
Olga Gnatenko

1
Doğru yaklaşım budur. Bir kez düşündüğünüzde apaçık ortada.
Dave

Benim için svg.select ('# links') ... çalışmak için d3.select ('# links') ... ile değiştirilmeliydi. Belki bu başkalarına yardımcı olur.
takas

26

SVG'nin Z-endeksi olmadığından, ancak DOM öğelerinin sırasını kullandığından, bunu şu şekilde öne getirebilirsiniz:

this.parentNode.appendChild(this);

Daha sonra, örneğin tekrar takmak için insertBefore'den yararlanabilirsiniz mouseout. Ancak bu, öğenizin daha önce eklenmesi gereken kardeş düğümünü hedefleyebilmenizi gerektirir.

DEMO: Şu JSFiddle'a bir göz atın


Bu, Firefox'un herhangi bir :hoverstili işleyememesi olmasaydı harika bir çözüm olurdu . Ayrıca bu kullanım durumu için sondaki ìnsert fonksiyonunun gerekli olacağını düşünmüyorum.
John Slegers

@swenedo, çözümünüz benim için harika çalışıyor ve öğeyi öne çıkarıyor. Arkaya getirmekle ilgili olarak, biraz yığıldım ve doğru şekilde nasıl yazacağımı bilmiyorum. Lütfen, nesneyi arkaya nasıl getirebileceğiniz konusunda bir örnek gönderebilir misiniz? Teşekkürler.
Mike B.

1
@MikeB. Elbette, cevabımı yeni güncelledim. D3'ün insertişlevinin muhtemelen en iyi yol olmadığını fark ettim , çünkü yeni bir unsur yaratıyor . Sadece var olan bir öğeyi taşımak istiyoruz, bu nedenle insertBeforebunun yerine bu örnekte kullanıyorum.
swenedo

Bu yöntemi kullanmayı denedim ama maalesef IE11'de çalışmıyorum. Bu tarayıcı için bir çözümünüz var mı?
Antonio Giovanni Schiavone

13

SVG, z-endeksi yapmaz. Z sırası, konteynerlerindeki SVG DOM öğelerinin sırasına göre belirlenir.

Anlayabildiğim kadarıyla (ve bunu geçmişte birkaç kez denedim), D3, tek bir öğeyi öne ya da ne getirecek şekilde ayırmak ve yeniden eklemek için yöntemler sağlamıyor.

Düğümleri seçimde göründükleri sırayla eşleştirmek için yeniden karıştıran bir .order()yöntem vardır . Sizin durumunuzda, tek bir unsuru öne getirmeniz gerekiyor. Böylece, teknik olarak, seçimi istediğiniz öğenin önünde (veya sonunda, hangisinin en üstte olduğunu hatırlayamıyorsanız) uygulayabilir ve sonra order()onu çağırabilirsiniz .

Veya bu görev için d3'ü atlayabilir ve o tek DOM öğesini yeniden eklemek için düz JS (veya jQuery) kullanabilirsiniz.


5
Bazı tarayıcılarda fareyle üzerine gelme işleyicisinin içindeki öğelerin kaldırılması / eklenmesiyle ilgili sorunlar olduğunu unutmayın; bu, fareyle çekme olaylarının düzgün şekilde gönderilmemesine neden olabilir, bu nedenle beklediğiniz gibi çalıştığından emin olmak için bunu tüm tarayıcılarda denemenizi öneririz.
Erik Dahlström

Sırayı değiştirmek pasta grafiğimin düzenini de değiştirmez mi? Öğeleri nasıl yeniden ekleyeceğimi öğrenmek için jQuery'de de araştırma yapıyorum.
Evil Closet Monkey

Sorumu, seçtiğim çözümle güncelledim, bu aslında orijinal sorunun özünü kullanmıyor. Bu çözümle ilgili gerçekten kötü bir şey görürseniz, yorumları memnuniyetle karşılıyoruz! Asıl soru hakkında fikir verdiğiniz için teşekkür ederiz.
Evil Closet Monkey

Makul bir yaklaşım gibi görünüyor, esasen üst üste gelen şeklin dolgusu olmaması nedeniyle. Olsaydı, fare olayını göründüğü anda "çalmasını" beklerdim. Ve bence @ ErikDahlström'ün noktası hala geçerli: bu hala bazı tarayıcılarda bir sorun olabilir (öncelikle IE9). Ekstra "güvenlik" .style('pointer-events', 'none')için, üst üste bindirilmiş yola ekleyebilirsiniz . Ne yazık ki, bu mülk IE'de hiçbir şey yapmıyor, ama yine de ....
meetamit

@EvilClosetMonkey Merhaba ... Bu soru çok fazla görüş alıyor ve geleceğin aşağıdaki cevabının benimkinden daha yararlı olduğuna inanıyorum. Bu yüzden, belki de kabul edilen cevabı gelecek için değiştirmeyi düşünün, böylece daha yüksek görünebilir.
meetamit


4

Geleceğin çözümünü koduma uyguladım ve işe yaradı, ancak kullandığım çok sayıda öğe ile çok yavaştı. Özel görselleştirmem için daha hızlı çalışan jQuery kullanan alternatif yöntem burada. Ortak bir sınıfa sahip olmasını istediğiniz svg'lere dayanır (benim örneğimde sınıf, veri kümemde d.key olarak belirtilmiştir). <g>Kodumda, yeniden düzenlediğim tüm SVG'leri içeren "konumlar" sınıfına sahip bir yer var.

.on("mouseover", function(d) {
    var pts = $("." + d.key).detach();
    $(".locations").append(pts);
 });

Dolayısıyla, belirli bir veri noktasının üzerine geldiğinizde, kod, söz konusu sınıfa sahip SVG DOM öğelerine sahip diğer tüm veri noktalarını bulur. Ardından, bu veri noktalarıyla ilişkili SVG DOM öğelerini çıkarır ve yeniden ekler.


4

Tamamen yeni bir cevap yazmak yerine @ notan3xit'in cevapladığı şeyi genişletmek istedim (ancak yeterli itibarım yok).

Eleman sırası problemini çözmenin başka bir yolu, çizim sırasında 'eklemek' yerine 'ekle' kullanmaktır. Bu şekilde, yollar her zaman diğer svg öğelerinden önce birlikte yerleştirilir (bu, kodunuzun diğer svg öğeleri için enter () işleminden önce bağlantılar için enter () işlevini zaten yaptığını varsayar).

d3 api ekle: https://github.com/mbostock/d3/wiki/Selections#insert


1

Mevcut bir SVG'de Z sırasını nasıl değiştireceğimi bulmak yaşlarımı aldı. Araç ipucu davranışı ile d3.brush bağlamında buna gerçekten ihtiyacım vardı. İki özelliğin birlikte güzel bir şekilde çalışması için ( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ), d3.brush'ın Z-sıralamasında ilk olması gerekir (Birincisi tuval üzerine çizilecek, daha sonra SVG öğelerinin geri kalanıyla kaplanacak) ve üstte ne olursa olsun (daha yüksek Z indisleri ile) tüm fare olaylarını yakalayacaktır.

Çoğu forum yorumu, önce kodunuza d3.brush'ı, ardından SVG "çizim" kodunuzu eklemeniz gerektiğini söyler. Ancak harici bir SVG dosyası yüklediğim için benim için bu mümkün olmadı. Fırçayı istediğiniz zaman kolayca ekleyebilir ve daha sonra aşağıdakilerle Z sırasını değiştirebilirsiniz:

d3.select("svg").insert("g", ":first-child");

Bir d3.brush kurulumu bağlamında şöyle görünecektir:

brush = d3.svg.brush()
    .x(d3.scale.identity().domain([1, width-1]))
    .y(d3.scale.identity().domain([1, height-1]))
    .clamp([true,true])
    .on("brush", function() {
      var extent = d3.event.target.extent();
      ...
    });
d3.select("svg").insert("g", ":first-child");
  .attr("class", "brush")
  .call(brush);

d3.js ekleme () işlev API'si: https://github.com/mbostock/d3/wiki/Selections#insert

Bu yardımcı olur umarım!


0

Versiyon 1

Teorik olarak, aşağıdakiler iyi çalışmalıdır.

CSS kodu:

path:hover {
    stroke: #fff;
    stroke-width : 2;
}

Bu CSS kodu, seçilen yola bir kontur ekleyecektir.

JS kodu:

svg.selectAll("path").on("mouseover", function(d) {
    this.parentNode.appendChild(this);
});

Bu JS kodu, önce yolu DOM ağacından kaldırır ve ardından onu üst öğesinin son alt öğesi olarak ekler. Bu, yolun aynı ebeveynin diğer tüm alt öğelerinin üzerine çizilmesini sağlar.

Pratikte, bu kod Chrome'da iyi çalışıyor ancak diğer bazı tarayıcılarda bozuluyor. Linux Mint makinemde Firefox 20'de denedim ve çalışmasını sağlayamadım. Her nasılsa, Firefox :hoverstilleri tetikleyemiyor ve ben bunu düzeltmenin bir yolunu bulamadım.


Versiyon 2

Bu yüzden bir alternatif buldum. Biraz 'kirli' olabilir, ancak en azından işe yarıyor ve tüm unsurların üzerinden geçmeyi gerektirmiyor (diğer bazı cevaplar gibi).

CSS kodu:

path.hover {
    stroke: #fff;
    stroke-width : 2;
}

:hoverSahte seçiciyi kullanmak yerine bir .hoversınıf kullanıyorum

JS kodu:

svg.selectAll(".path")
   .on("mouseover", function(d) {
       d3.select(this).classed('hover', true);
       this.parentNode.appendChild(this);
   })
   .on("mouseout", function(d) {
       d3.select(this).classed('hover', false);
   })

Fareyle üzerine gelindiğinde, .hoversınıfı yoluma eklerim. Fare üzerinden çekildiğinde kaldırırım. İlk durumda olduğu gibi, kod ayrıca yolu DOM ağacından kaldırır ve ardından onu üst öğesinin son alt öğesi olarak ekler.


0

Bunu Fare Üzerinden Yapabilirsin Yukarıya Çekebilirsin.

d3.selection.prototype.bringElementAsTopLayer = function() {
       return this.each(function(){
       this.parentNode.appendChild(this);
   });
};

d3.selection.prototype.pushElementAsBackLayer = function() { 
return this.each(function() { 
    var firstChild = this.parentNode.firstChild; 
    if (firstChild) { 
        this.parentNode.insertBefore(this, firstChild); 
    } 
}); 

};

nodes.on("mouseover",function(){
  d3.select(this).bringElementAsTopLayer();
});

Geriye İtmek İstiyorsan

nodes.on("mouseout",function(){
   d3.select(this).pushElementAsBackLayer();
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.