# Triggering tween conditionally on different attributes and running them in parallel

**URL:** <https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948>\
**Category:** Help\
**Created:** [May 17, 2023, 10:17pm UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948 "2023-05-17T22:17:10Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 17, 2023, 10:17pm UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/1 "2023-05-17T22:17:10Z")

</div>

I am trying to trigger a **`stroke-dashoffset`** animation conditionally based on an existing **`stroke-dasharray`** animation. But when the **`stroke-dashoffset`** tween kicks in; the existing **`stroke-dasharray`** does not tween anymore. How can I run both of them in parallel? My original intention is to author the tween to kick off with **#1 `stroke-dasharray`** and keep on checking the `interpolatedValue`. When the `interpolatedValue` is `>` than 10% of total pathLength to trigger **#2`stroke-dashoffset`** without stopping the existing **#1 `stroke-dasharray`**. The following code starts with #1, triggers #2 **AND** stops #1; which I don’t want.

It is based on [this](https://observablehq.com/@d3/learn-d3-animation)

```
const path =
    bound
    .append("path")
    .classed('path', true)
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "white")
    .attr("stroke-width", 1.5)
    .attr("d", d3.line()
        .x(d => scaleX(d.date))
        .y(d => scaleY(d.close))
    )
    .transition()
    .duration(8000)
    .attrTween('stroke-dasharray', function() {
            const length = this.getTotalLength();
            return function(t) {
                const interpolatedValue = d3.interpolate(0, length)(t);
                return `0,${length}`, `${interpolatedValue},${length}`
            }
        }

    )
    .attrTween('stroke-dashoffset', function() {
        const length = this.getTotalLength();
        const lengthPct = length * 0.1;
        return function(t) {
            const interpolatedValue = d3.interpolate(0, length)(t);
            if (interpolatedValue > lengthPct) {
                // Trigger the stroke-dashoffset animation               
                const dashOffset = length * -1;
                d3.select(this)
                    .attr('stroke-dashoffset', dashOffset)
                    .transition()
                    .duration(8000)
                    .attrTween('stroke-dashoffset', function() {
                        return d3.interpolate(0, dashOffset);
                    })
            }
        }
    }) 

```

Thank you in advance  
@Fil

---

<div class="post-metadata">

**Author:** ![Fil](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/fil/32/207_2.png) [@Fil](https://talk.observablehq.com/u/Fil)\
**Post date:** [May 18, 2023, 9:35am UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/2 "2023-05-18T09:35:52Z")

</div>

I pasted the code in a notebook, added the missing pieces (scales, etc), and it seems to do something, but I’m not sure about your intent. [Animation control / recifs | Observable](https://observablehq.com/d/1e0ca63a6a302d80)

---

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 18, 2023, 12:38pm UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/3 "2023-05-18T12:38:32Z")

</div>

Thanks @Fil. If you take a look at the inspector, you will see, once strok-dashoffset kicks in stroke-dasharray twen does not happen any more. Following is the snapshot from the end of animation. I want stroke-daharray’s interpolation to continue and end at `3729.6455078 3729.6455078`

![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/8/85b9880742a8d7d46c0be2b751a888c567c35a4f.png)

---

<div class="post-metadata">

**Author:** ![Fil](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/fil/32/207_2.png) [@Fil](https://talk.observablehq.com/u/Fil)\
**Post date:** [May 19, 2023, 12:20pm UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/4 "2023-05-19T12:20:54Z")

</div>

I’ve tried to guess what you’re trying to obtain (visually), and updated the code to reflect my guess.

Note that in [the life of a transition](https://github.com/d3/d3-transition#the-life-of-a-transition) it is documented that “When the transition (…) starts, it interrupts the active transition of the same name on the same element”, which explains why, in your original code, the first transition is interrupted.

---

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 19, 2023, 8:18pm UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/5 "2023-05-19T20:18:09Z")

</div>

@Fil thanks for this. But this is not what I am asking.

I am asking if I am able to trigger `stroke-dashoffset` at a certain stage of the existing `stroke-dasharray` tween (let’s suppose when `stroke-dasharray` tween is 50% done) how can I not have the existing `stroke-dasharray` tween interrupted at all, so that both the tween runs in parallel.

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/0/0a92632634f2bfcaf1108f7f790f78b4f2013216.png).

You mentioned - When the transition (…) starts, it interrupts the active transition _ **of the same name on the same element** _. I initially thought that I would need two different named transitions on the same element because I hoped to have them execute as desired with two different names on the same element. I tried this which also did not work.

```
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>
    <div id="viz"></div>
</body>
<script>
const width = 1280;
const height = 600;

const main = d3.select('div')
    .style('position', 'relative');

//namespace
const svgns = 'http://www.w3.org/2000/svg'

main
    .append('svg')
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)
    .attr('id', 'svg')

const svg = d3.select('svg')

svg.append('rect')
    .attr('class', 'vBoxRect')
    .attr('width', `${width}`)
    .attr('height', `${height}`)       
    .attr('fill', 'none')
    .attr('stroke', 'red');

// //------------------------2. CREATE BOUND------------------------//    
const padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 50
};

const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)        
    .attr('fill', 'none')
    .attr('stroke', 'green')

//create bound element
const bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`);

bound.append('g')
    .classed('vertical', true)
    .append('line')
    .attr('x1', '580')
    .attr('x2', '580')
    .attr('y1', '0')
    .attr('y2', '480')
    .attr('stroke', 'blue');

const element =
    bound.append('g')
    .classed('horizontal', true)
    .append('line')
    .attr('x1', '0')
    .attr('x2', '1160')
    .attr('y1', '240')
    .attr('y2', '240')
    .attr('stroke', 'red');

const tranOne = d3.transition()
    .duration(4000)
    .ease(d3.easeLinear);

element
    .transition(tranOne)
    // .duration(4000)
    // .ease(d3.easeLinear)
    .attrTween('stroke-dasharray', function() {
            const length = this.getTotalLength();
            return function(t) {
                const interpolatedValue = d3.interpolate(0, length)(t);
                return `0,${length}`, `${interpolatedValue},${length}`
            }
        }

    );
const tranTwo = d3.transition()
    .duration(4000)
    .ease(d3.easeLinear);

element
    .transition(tranTwo)
    // .duration(4000)
    // .ease(d3.easeLinear)
    .delay(function(a, i) {
        const element = this;
        const len = this.getTotalLength();
        const pct = 0.5;
        const pctPoint = element.getPointAtLength(len * pct);
        const pctStart = element.getPointAtLength(len * 0);
        const d = 1;
        const p = pctPoint.x; 
        const c = len;
        const b = pctStart.x;
        const t = ((p - b) / c) * d;
        return 4000 * t

    })
    .attrTween(
        'stroke-dashoffset',
        function(a, i) {
            const element = this;
            const len = this.getTotalLength();
            const dashOffset = len * -1;
            return function(t) {
                const interpolatedValue = d3.interpolate(0, dashOffset)(t);
                return interpolatedValue;
            }
        }
    );
</script>

</html>

```

---

<div class="post-metadata">

**Author:** ![Fil](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/fil/32/207_2.png) [@Fil](https://talk.observablehq.com/u/Fil)\
**Post date:** [May 21, 2023, 8:47am UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/6 "2023-05-21T08:47:51Z")

</div>

I’ve updated [Animation control / recifs | Observable](https://observablehq.com/@recifs/animation-control--support) with two functions that map the interval [0, 1] that corresponds to the full transition support ([0, 6000ms]) to each of the target intervals. I believe this supports what your drawing shows. I’m still not sure of what you’re trying to achieve visually, but hopefully gives you enough to draw a conclusion?

---

<div class="post-metadata">

**Author:** ![smpa01](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/smpa01/32/5352_2.png) [@smpa01](https://talk.observablehq.com/u/smpa01)\
**Post date:** [May 21, 2023, 9:43pm UTC](https://talk.observablehq.com/t/triggering-tween-conditionally-on-different-attributes-and-running-them-in-parallel/7948/7 "2023-05-21T21:43:53Z")

</div>

This is awesome !!! @Fil many thanks, such a clever use of custom interpolator through scale linear.
