3

So since some time Youtube has the functionality to minimize the Youtube Player while navigating through the page, LolEsports.com has a similar functionality with Twitch and the Embeded Youtube Player.

I wanna bring a similar functionaltiy to my page basicly my state looks like this:

  • Vue in the Frontend
  • Embdeded Youtube Player on a suburl
  • When I leave the suburl I want the Youtube Player to move into a corner with its current state and still be there

I would like to hear some inspiration on how I could achieve that since when I tried out the state reset when passing it on a different component and also the playing stopped etc.

Would love to hear ideas

niclas_4
  • 3,514
  • 1
  • 18
  • 49

2 Answers2

2

The short answer is: You cannot move iframe DOM nodes without losing its state, as outlined in this answer.

The long answer is: You have alternatives. I think your best bet is to play the video directly in the "corner player", then restyle it to be on the right place of the page. To do so you will need to interact with the DOM yourself a bit, which you can do easily by setting a ref on the container you want the video to appear in.

In the end you would end up with something like this:

In your App.vue you have your standard router-view, as well as some element that will contain your actual video.

<template>
  <div id="app">
    <router-view/>
    <corner-player/>
  </div>
</template>

Your page that will start the video will contain some placeholder element that will serve as a guiding point where to put the video:

<div class="player-container" ref="player"></div>

We then use lifecycle hooks to get the correct url and styling to send to the player. I use a simple bus to more easily pass events around the application, as it would generally be a hassle to get the data to the player component otherwise. We use the mounted hook to set up the video, because that is when the first render cycle has taken place. The beforeDestroy lifecycle allows us to send some kind of event that will put the video in the corner.

export default {
  name: "child1",

  mounted() {
    const container = this.$refs.player;
    const boundingBox = container.getBoundingClientRect();

    this.$bus.$emit("play-video", {
      url: "https://www.youtube.com/embed/dW4HCi1zZh8",
      styling: {
        position: "absolute",
        top: `${boundingBox.top}px`,
        left: `${boundingBox.left}px`,
        width: `${boundingBox.width}px`,
        height: `${boundingBox.height}px`,
        border: "3px dotted red"
      }
    });
  },

  beforeDestroy() {
    this.$bus.$emit("move-to-corner-player");
  }
};

The player component then consumes these two events and makes sure that the correct styling is applied. I would not recommend applying raw css like I do here, but this is a proof of concept.

<template>
  <div class="corner-player" :style="styling" v-if="url">
    <iframe
      width="1280"
      height="540"
      :src="url"
      frameborder="0"
      allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen
    ></iframe>
  </div>
</template>

<script>
export default {
  name: "CornerPlayer",

  data() {
    return {
      styling: {},
      url: "",

      defaultStyling: {
        border: "3px solid blue",
        position: "fixed",
        bottom: 0,
        right: 0,
        height: "150px",
        width: "220px"
      }
    };
  },

  created() {
    this.$bus.$on("play-video", this.playVideo);
    this.$bus.$on("move-to-corner-player", this.moveToCornerPlayer);
  },

  beforeDestroy() {
    // Prevent memory leaks
    this.$bus.$off("play-video", this.playVideo);
    this.$bus.$off("move-to-corner-player", this.moveToCornerPlayer);
  },

  methods: {
    playVideo({ url, styling }) {
      this.url = url;
      this.styling = styling;
    },

    moveToCornerPlayer() {
      this.styling = {
        ...this.defaultStyling
      };
    }
  }
};
</script>

<style scoped>
iframe {
  width: 100%;
  height: 100%;
}
</style>

You can play with this on Codesandbox:

Edit Pass an Iframe to different Components in an SPA (similar to Youtube)

Sumurai8
  • 20,333
  • 11
  • 66
  • 100
  • Thank you a lot very interesting approach, I tried to bring it into my App and did so successfully, I decided against a Event Bus and went for Vuex but I asume you would have done the same if it weren´t for simplicity to show your example. – niclas_4 Jun 21 '19 at 18:45
  • Probably will use EventBus for Events like Skipping forward via Timestamps since that doesnt work without watchers and stuff with the store, unless there is a better way then let me know :) – niclas_4 Jun 21 '19 at 18:55
  • Yes, Vuex will work just as well. You will be handling the state in a vuex module instead of the component in that case. Other than that the same still applies. I agree that using events to skip around the video is probably the best option, especially if you use some kind of library to interact with the player instead of just reloading the iframe with some magic url. – Sumurai8 Jun 21 '19 at 21:11
0

Thanks Sumurai8! To make the iframe container more responsive, I edited your CodeSandbox a bit: https://codesandbox.io/s/pass-an-iframe-to-different-components-forked-jeipq?file=/src/components/Child1.vue

So now the container will scale and stay at the same position when scrolling and refreshing.

Arwin
  • 1
  • 2