0

I am writing some content in bullet points in HTML and then using swiper.js library ( https://swiperjs.com/demos/255-effect-cards/core) to display them as sliders. I am giving a download button to the slider and want the user to download all those sliders at once which are written inside HTML divs

Can you help me with how can make it work?

  • User sees slider
  • The user clicks on the download
  • It should convert all N slides into image format files(png/jpeg) and download each image to the user's device.

code for js slider which has multiple divs and content inside each div

<div class="swiper mySwiper" >
  <div class="swiper-wrapper">
    <div class="swiper-slide">
      <h3  #screen>{{summary[0]}}</h3>
    </div>
    <div class="swiper-slide" >
      <h3 #screen>{{summary[1]}}</h3>
    </div>
    <div class="swiper-slide">
      <h3>{{summary[2]}}</h3>
    </div>
    <div class="swiper-slide">
      <h3>{{summary[3]}}</h3>
    </div>
    <div *ngIf="summary[4] != null" class="swiper-slide">
      <h3>{{summary[4]}}</h3>
    </div>
  </div>
</div>  

converting each div into an image

<div id="download" class="main" *ngIf="image">
  <img #canvas>
  <a #downloadLink></a>
</div> 
@ViewChild('screen') screen: ElementRef;
@ViewChild('canvas') canvas: ElementRef;
@ViewChild('downloadLink') downloadLink: ElementRef;

when user clicks on the button it should download all of the slides in one go

html2canvas(this.screen.nativeElement).then((canvas: any) => {
  this.canvas.nativeElement.src = canvas.toDataURL();
  this.downloadLink.nativeElement.href = canvas.toDataURL('image/png');
  this.downloadLink.nativeElement.download = 'summary.png';
  this.downloadLink.nativeElement.click();
});
Alexis
  • 1,685
  • 1
  • 12
  • 30
Prabhakar
  • 6,458
  • 2
  • 40
  • 51
  • try giving uniqe local reference to all #screen, loop through all screen for `html2canvas(screen)`, to give localref in loop check : https://stackoverflow.com/questions/48347854 – vinesh_dodiya Apr 03 '23 at 01:00
  • 3
    I'm not entirely sure, but it seems you have all the building parts that you need to implement your full solution. What is the specific problem you're struggling with here? – JoannaFalkowska Apr 04 '23 at 14:33
  • 1
    Hi, one way could be to use [JSZip](https://stuk.github.io/jszip/) .... – Anant V Apr 06 '23 at 05:17

2 Answers2

1

As already commented by Anant V jszip.js can combine multiple files to a single download.

The slightly tricky parts:

  • due to this specific "card deck" swiper style we need to temporarily reset transformations – otherwise, the canvas to image output will return cropped images.
  • a lot of asynchronous calls (await for the canvas rendering, wait for the blob creation etc.)

The download functionality doesn't work in SO snippets:
See working codepen example

// init swiper
const swiper = new Swiper(".mySwiper", {
  effect: "cards",
  grabCursor: true
});
// wait for all fonts
document.fonts.ready.then(function () {
  // convert slides to image blobs and zip them
  zipImagesInBackground(mySwiper, aDownload, 2, "myswiperDownload.zip");
});
async function zipImagesInBackground(
  slider,
  downloadEl,
  scale = 1,
  filename = "example.zip"
) {
  // init zip object
  let zip = new JSZip();
  // create canvas for each slide
  let slides = slider.querySelectorAll(".swiper-slide");
  for (let i = 0; i < slides.length; i++) {
    let slide = slides[i];
    // temporarily disable transformation for canvas drawing
    let style = window.getComputedStyle(slide);
    let transform = style.transform;
    slide.style.transform = "none";
    let canvas = await html2canvas(slide, {
      scale: scale
    });
    let dataUrl = await canvas.toDataURL();
    let base64 = dataUrl.split("base64,")[1];
    zip.file(`slide${i}.png`, base64, {
      base64: true
    });
    //re-apply transform
    slide.style.transform = transform;
  }
  slider.classList.remove("hidden");
  // create zip
  let blob = await zip.generateAsync({
    type: "blob"
  });
  downloadEl.href = URL.createObjectURL(blob);
  downloadEl.download = filename;
}
body {
  font-family: "Noto Sans";
  font-weight: 400;
}

.swiper {
  transition: 0.3s;
}
.hidden {
  opacity: 1;
}

.btn-default {
  text-decoration: none;
  border: 1px solid #ccc;
  padding: 0.3em;
  color: inherit;
}

.layout {
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  height: 100%;
}

.swiper {
  width: 240px;
  height: 320px;
}

.swiper-slide {
  display: flex !important;
  align-items: center;
  justify-content: center;
  border-radius: 18px;
  font-size: 22px;
  font-weight: bold;
  color: #fff;
}

.swiper-slide:nth-child(1n) {
  background-color: rgb(206, 17, 17);
}

.swiper-slide:nth-child(2n) {
  background-color: rgb(0, 140, 255);
}

.swiper-slide:nth-child(3n) {
  background-color: rgb(10, 184, 111);
}

.swiper-slide:nth-child(4n) {
  background-color: rgb(211, 122, 7);
}

.swiper-slide:nth-child(5n) {
  background-color: rgb(118, 163, 12);
}

.swiper-slide:nth-child(6n) {
  background-color: rgb(180, 10, 47);
}

.swiper-slide:nth-child(7n) {
  background-color: rgb(35, 99, 19);
}

.swiper-slide:nth-child(8n) {
  background-color: rgb(0, 68, 255);
}

.swiper-slide:nth-child(9n) {
  background-color: rgb(218, 12, 218);
}

.swiper-slide:nth-child(10n) {
  background-color: rgb(54, 94, 77);
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css" />
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans&display=swap" rel="stylesheet">

<p><a id="aDownload" class="btn-default" download="slides.zip" href="">Download Slides</a></p>

<div class="layout">
  <div id="mySwiper" class="swiper mySwiper hidden">
    <div class="swiper-wrapper">
      <div class="swiper-slide">Slide 1</div>
      <div class="swiper-slide ">Slide 2</div>
      <div class="swiper-slide">Slide 3</div>
      <div class="swiper-slide">Slide 4</div>
      <div class="swiper-slide">Slide 5</div>
      <div class="swiper-slide">Slide 6</div>
      <div class="swiper-slide">Slide 7</div>
      <div class="swiper-slide">Slide 8</div>
      <div class="swiper-slide">Slide 9</div>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.2.2/dist/jszip.js"></script>

How it works:

  1. loop through all slides and create images via html2canvas
  2. create blobs for each image and collect it in a zip object provided by jszip.js
  3. create an object URL for the download link/button
herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
  • thanks, for your answer. Is it possible to download these images without zip. Right now, this code is downloading as one zip file. When the user clicks on download? it should just download all images into the phone gallery or laptop( no zipfile) – Prabhakar Apr 10 '23 at 07:57
  • You could run multiple downloads, but you would need to confirm each download. See [Download multiple files with a single action](https://stackoverflow.com/questions/2339440/download-multiple-files-with-a-single-action#36999563). Imagine the consequences if this was possibble without additional prompts: you could exploit such a behaviour to download malicious files in background. So zipping the files (or using another archive format) is currently the only way to do this in a browser. – herrstrietzel Apr 10 '23 at 15:12
0

I've used this library before, but it only works for saving images written to the canvas. documentação da biblioteca. eligrey/FileSaver

HTML div class="swiper-slide"><img src='pic_the_scream.jpg'

JAVASCRIPT

function saveIMG(){
var div = document.getElementsByClassName('swiper-slide-active');//obtem nó com a imagem ativa
var image = div[0].getElementsByTagName('img')[0];

var canva = document.createElement("canvas");//cria um elemento canvas
canva.width = image.naturalWidth;//define largura do object criado proporcional a imagem
canva.height = image.naturalHeight;//define altura do object criado proporcional a imagem

var title = 'slide_'+document.getElementsByClassName('swiper-slide-active')[0].getAttribute('aria-label').substring(0,document.getElementsByClassName('swiper-slide-active')[0].getAttribute('aria-label').indexOf('/')-1)+'.png';//Nome do arquivo criado

canva.getContext("2d").drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight, 0, 0, image.naturalWidth, image.naturalHeight);//desenha a image dentro do elemento canvas

canva.toBlob(function(blob){
   saveAs(blob, title); //salva a imagem gerada no elemento canva criado
});

};

meustestes: This is the github repository I used for testing. To see the code working, see the repository on github that let.

There may be cross contamination issues. The code gives error when I try to run inside the machine.

The images need to be inside the origin server, but if you try to apply the code below, the code saves an empty image.

// Solving the contamination problem
var img = new Image();
img.src = image.src;    
img.crossOrigin = 'anonymous';

But if I run the code inside w3schools , the image origin server , the image is downloaded intact.

Good luck resolving this issue.

RhaianySouza
  • 116
  • 2