Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div class="image-banner">
  <canvas class="image-banner__grayscale"></canvas>
  <img class="image-banner__img" src="https://images.unsplash.com/photo-1556629538-fc3eba61504e?auto=format&fit=crop&w=1300&q=80" alt="Cityscape" crossorigin="anonymous">
</div>
              
            
!

CSS

              
                * {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.download-link {
  display: block;
  position: fixed;
  z-index: 100;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 60px;
  line-height: 60px;
  text-align: center;
  background: green;
  color: white;
  text-decoration: none;
  font-size: 1rem;
  font-weight: 600;
}

.image-banner {
  background: #000;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  
  &__img {
    position: relative;
    max-height: 100%;
    width: auto;
    opacity: 1;
    transition: opacity 1s ease-out;
    
    .flipped--grayscale & {
      opacity: 0;
    }
  }
  
  &__grayscale {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    width: 100%;
    height: 100%;
    margin: auto;
  }
    
  &::after {
    content: 'Click to toggle grayscale';
    color: transparentize(#fff, .3);
    width: 100%;
    position: absolute;
    top: 5%;
    left: 0;
    bottom: 0;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    font-size: 0.875rem;
    font-weight: 600;
    animation: blink 3s linear infinite;
    
    @keyframes blink {
      0%, 100% {
        opacity: 1;
      }
      35%, 65% {
        opacity: 0;
      }
    }
  }
}
              
            
!

JS

              
                const banner = document.querySelector('.image-banner');
const img = banner.querySelector('img');
const canvas = banner.querySelector('canvas');

function downloadBlob(blob, filename) {
  // Create an object URL for the blob object
  const url = URL.createObjectURL(blob);
  
  // Create a new anchor element
  const a = document.createElement('a');
  
  // Set the href and download attributes for the anchor element
  // You can optionally set other attributes like `title`, etc
  // Especially, if the anchor element will be attached to the DOM
  a.href = url;
  a.download = filename || 'download';
  
  // Click handler that releases the object URL after the element has been clicked
  // This is required for one-off downloads of the blob content
  const clickHandler = function() {
    setTimeout(() => {
      // Release the object URL
      URL.revokeObjectURL(url);

      // Remove the event listener from the anchor element
      this.removeEventListener('click', clickHandler);

      // Remove the anchor element from the DOM
      (this.remove && (this.remove(), 1)) ||
      (this.parentNode && this.parentNode.removeChild(this));
    }, 150)
  };
  
  // Add the click event listener on the anchor element
  // a.addEventListener('click', clickHandler, false);
  
  // Programmatically trigger a click on the anchor element
  // Useful if you want the download to happen automatically
  // Without attaching the anchor element to the DOM
  // a.click();
  
  // Return the anchor element
  // Useful if you want a reference to the element
  // in order to attach it to the DOM or use it in some other way
  return a;
}

function resolveCanvasDimensions() {
  const { width, height } = window.getComputedStyle(img);
  
  canvas.width = +parseFloat(width);
  canvas.height = +parseFloat(height);
  
  canvas.style.width = width;
  canvas.style.height = height;
}

function processGrayscaleImage() {
  const ctx = canvas.getContext('2d');
  const { width, height } = canvas;
  
  ctx.drawImage(img, 0, 0, width, height);
  
  const imageData = ctx.getImageData(0, 0, width, height);
  const data = imageData.data;
  
  for (let i = 0, len = data.length; i < len; i += 4) {
    const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
    
    data[i]     = avg; // red
    data[i + 1] = avg; // green
    data[i + 2] = avg; // blue
  }

  ctx.putImageData(imageData, 0, 0);
  
  canvas.toBlob(blob => {
    const downloadLink = downloadBlob(blob);
    
    // Set the title and classnames of the link
    downloadLink.title = 'Download Grayscale';
    downloadLink.classList.add('btn-link', 'download-link');
    
    downloadLink.textContent = 'Download Grayscale';

    // Attach the link to the DOM
    document.body.appendChild(downloadLink);
  });
}

function initialize() {
  img.addEventListener('load', () => {
    resolveCanvasDimensions();
    processGrayscaleImage();

    banner.addEventListener('click', () => {
      banner.classList.toggle('flipped--grayscale');
    }, false);
  }, false);
}

initialize();
              
            
!
999px

Console