How to get a torn paper effect with random saw teeth?

Solution 1:

This can be done using svg. I am using Snap and jquery to make it as it makes everything a lot easier.

The following snippets create random torn paper effects.

Note:Support for snap - IE9 and up, Safari, Chrome, Firefox, and Opera see the specs

Support for svg caniuse

$(document).ready(function() {
  var s = Snap('svg');
  var width = $('.content').outerWidth();
  $('.effect').width(width);
  var lastX = 0;
  var pointsArray = [0, 0];
  while (lastX <= width) {
    var randX = Math.floor(Math.random() * 25);
    var randY = Math.floor(Math.random() * 30);
    pointsArray.push(randX + lastX + ' ' + randY);
    lastX = lastX + randX;
  }
  var torn = s.polygon(pointsArray + " " + width + " 0").attr({
    fill: 'orange'
  })
})
.content {
  width: 400px;
  height: 400px;
  background: orange;
  padding: 20px;
}
.effect {
  height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<div class="torn">
  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
  <div class="effect">
    <svg width="100%" height="100%"></svg>
  </div>
</div>

How this works ?

First an array is created to hold the coordinates that are randomly created by jquery. x and y coordinates are created using Math.random() and are added to the array.Before adding to the array the current x coordinate is added to the last x coordinate received. This is to make it continuous.

Changing the 10 in the randX variable allows us to increase or decrease the length of one line and changing the 30 in the randY variable allows to change the height of one line.

Here is a snippet that uses low randX

$(document).ready(function() {
  var s = Snap('svg');
  var width = $('.content').outerWidth();
  $('.effect').width(width);
  var lastX = 0;
  var lastY = 0;
  var pointsArray = [0, 0];
  while (lastX <= width) {
    var randX = Math.floor(Math.random() * 2);
    var randY = Math.floor(Math.random() * 30);
    pointsArray.push(randX + lastX + ' ' + randY);
    lastX = lastX + randX;
  }
  var torn = s.polygon(pointsArray + " " + width + " 0").attr({
    fill: 'orange'
  })
})
.content {
  width: 400px;
  height: 400px;
  background: orange;
  padding: 20px;
}
.effect {
  height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<div class="torn">
  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
  <div class="effect">
    <svg width="100%" height="100%"></svg>
  </div>
</div>

Would like to have one with border?

Change polygon to polyline and add stroke

$(document).ready(function() {
  var s = Snap('svg');
  var width = $('.content').outerWidth();
  $('.effect').width(width);
  var lastX = 0;
  var lastY = 0;
  var pointsArray = [0, 0];
  while (lastX <= width) {
    var randX = Math.floor(Math.random() * 20);
    var randY = Math.floor(Math.random() * 30);
    pointsArray.push(randX + lastX + ' ' + randY);
    lastX = lastX + randX;
  }
  var torn = s.polyline(pointsArray + " " + (width - 3) + " 0").attr({
    fill: 'orange',
    'stroke': 'black',
    'stroke-width': 3
  })
})
.torn {
  margin: 50px;
}
.content {
  width: 400px;
  height: 400px;
  background: orange;
  padding: 20px;
  border: 3px solid #000;
  border-bottom: 0;
}
.effect {
  height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<div class="torn">
  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
  <div class="effect">
    <svg width="100%" height="100%"></svg>
  </div>
</div>

Don't like random torn effects ?

i would suggest picking a nice torn effect from the random ones and copying its html like i have did here

.torn {
  margin: 50px;
}
.content {
  width: 400px;
  height: 400px;
  background: orange;
  padding: 20px;
}
.effect {
  height: 50px;
}
<div class="torn">
  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
  <div class="effect" style="width: 440px;">
    <svg width="100%" height="100%">
      <desc>Created with Snap</desc>
      <defs></defs>
      <polygon points="0,0,10 25,20 15,27 21,43 24,51 14,70 9,84 25,88 18,96 11,100 20,113 6,117 5,123 1,136 25,151 29,157 4,166 29,181 2,197 28,212 8,226 8,232 12,240 8,254 16,270 5,279 26,291 5,304 0,307 5,325 26,329 18,347 3,360 5,372 26,382 9,393 21,406 27,411 8,415 4,429 8,441 19 437 0"
      fill="#ffa500"></polygon>
    </svg>
  </div>
</div>

Need background images ?

Add an image using snap, set it's y coordinates to -440 (ie,height of the content.400 if padding is avoided) and clip it using the polygon

$(document).ready(function() {
  var s = Snap('svg');
  var width = $('.content').outerWidth();
  $('.effect').width(width);
  var lastX = 0;
  var lastY = 0;
  var pointsArray = [0, 0];
  while (lastX <= width) {
    var randX = Math.floor(Math.random() * 20);
    var randY = Math.floor(Math.random() * 30);
    pointsArray.push(randX + lastX + ' ' + randY);
    lastX = lastX + randX;
  }
  var img = s.image('https://placeimg.com/440/500/any', 0, -440, 440, 500)
  var torn = s.polygon(pointsArray + " " + (width - 3) + " 0").attr({

  })
  img.attr({
    'clip-path': torn
  })
})
.torn {
  margin: 50px;
}
.content {
  width: 400px;
  height: 400px;
  background: orange;
  padding: 20px;
  background: url('https://placeimg.com/440/500/any');
}
.effect {
  height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<div class="torn">
  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
  <div class="effect">
    <svg width="100%" height="100%"></svg>
  </div>
</div>

Solution 2:

Using Clip Paths:

The torn paper effect can also produced using clip-path It can be done with only HTML and CSS but the pure CSS version wouldn't produce a random clip effect like we can achieve using SNAP or other SVG libraries but it does produce a torn paper effect.

The drawback of using CSS clip-path is that it is currently supported only in Webkit powered browsers. Firefox supports only the url() syntax and so needs inline SVGs while it has absolutely no support in IE. [Can I Use]

.torn-paper{
  height: 300px;
  width: 400px;
  background: tomato;
  -webkit-clip-path: polygon(0% 0%, 0% 97%, 2% 95%, 6% 99%, 12% 88%, 18% 92%, 27% 90%, 31% 97%, 39% 91%, 47% 95%, 60% 83%, 62% 81%, 69% 93%, 72% 96%, 79% 87%, 100% 99%, 100% 0%);
  clip-path: polygon(0% 0%, 0% 97%, 2% 95%, 6% 99%, 12% 88%, 18% 92%, 27% 90%, 31% 97%, 39% 91%, 47% 95%, 60% 83%, 62% 81%, 69% 93%, 72% 96%, 79% 87%, 100% 99%, 100% 0%);  
}
<div class='torn-paper'>Lorem ipsum dolor sit amet</div>

As the clip-path is percentage based, it is by default responsive and it can work when the container div has a background image also.

.torn-paper{
  height: 300px;
  width: 400px;
  background: url(http://lorempixel.com/400/300);
  -webkit-clip-path: polygon(0% 0%, 0% 97%, 2% 95%, 6% 99%, 12% 88%, 18% 92%, 27% 90%, 31% 97%, 39% 91%, 47% 95%, 60% 83%, 62% 81%, 69% 93%, 72% 96%, 79% 87%, 100% 99%, 100% 0%);
  clip-path: polygon(0% 0%, 0% 97%, 2% 95%, 6% 99%, 12% 88%, 18% 92%, 27% 90%, 31% 97%, 39% 91%, 47% 95%, 60% 83%, 62% 81%, 69% 93%, 72% 96%, 79% 87%, 100% 99%, 100% 0%);  
}
<div class='torn-paper'>Lorem ipsum dolor sit amet</div>

If we really want a random torn paper effect then we can set the co-ordinates of the polygon clip path using JS and then add it as an inline style like in the below snippet. The snippet uses a logic similar to that in your answer to populate the array.

var el = document.getElementsByClassName('torn-paper')[0];

var lastX = 0,
  randX, randY, polygonPoints = ["0% 0%"];

randY = Math.floor(Math.random() * 20) + 80;

polygonPoints.push(lastX + '% ' + randY + '%');

while (lastX <= 100) {
  randX = Math.floor(Math.random() * 5);
  randY = Math.floor(Math.random() * 10) + 85;
  polygonPoints.push(randX + lastX + '% ' + randY + '%');
  lastX = lastX + randX;
}
polygonPoints.push("100% 0%");

el.style['-webkit-clip-path'] = 'polygon(' + polygonPoints.join() + ')';
el.style['clip-path'] = 'polygon(' + polygonPoints.join() + ')';
.torn-paper {
  height: 300px;
  width: 400px;
  background: tomato;
}
0
<div class='torn-paper'>Lorem ipsum dolor sit amet</div>

I hadn't made the following as my primary answer because SVGs were already covered in a different way in Akshay's answer but using inline SVG for the clip-path would work in Firefox also. IE still won't support it.

var el = document.getElementsByClassName('torn-paper')[0];
var path = document.getElementById('clipper-path');

var lastX = 0,
  randX, randY, polygonPoints = ["0 0"];

randY = (Math.floor(Math.random() * 20) + 80) / 100;

polygonPoints.push(lastX + ' ' + randY + '');

while (lastX <= 1) {
  randX = Math.floor(Math.random() * 5) / 100;
  randY = (Math.floor(Math.random() * 10) + 85) / 100;
  polygonPoints.push(randX + lastX + ' ' + randY + '');
  lastX = lastX + randX;
}
polygonPoints.push("1 0");

path.setAttribute('d', 'M' + polygonPoints.join() + 'z');
.torn-paper {
  height: 300px;
  width: 400px;
  background: tomato;
  -webkit-clip-path: url(#clipper);
  clip-path: url(#clipper);
}
<svg width="0" height="0">
  <defs>
    <clipPath id="clipper" clipPathUnits="objectBoundingBox">
      <path d='M0 0, 1 0, 1 1, 0 1z' id='clipper-path' />
    </clipPath>
  </defs>
</svg>
<div class="torn-paper">Lorem ipsum dolor sit amet</div>

Using Canvas:

I know that you haven't tagged Canvas but if you are looking for support in IE also then using Canvas would also be a good option. Canvas has very good browser support (same as SVG). I am including it here just as another option that could be used.

The approach is very much the same as explained earlier because here also we are creating a path and then clipping the canvas based on the path.

The below snippets are tested in IE9+, Edge, Firefox, Chrome, Safari and Opera.

var canvas = document.getElementById('torn-canvas');
var ctx = canvas.getContext('2d');
var lastX = 0,
  randX, randY;

ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);

randY = (Math.floor(Math.random() * 10) + 85) / 100 * canvas.height;
ctx.lineTo(0, randY);

while (lastX <= canvas.width) {
  randX = (Math.floor(Math.random() * 7.5)) / 100 * canvas.width;
  randY = (Math.floor(Math.random() * 10) + 85) / 100 * canvas.height;
  lastX = lastX + randX;
  ctx.lineTo(lastX, randY);
}
ctx.lineTo(canvas.width, 0);
ctx.closePath();
ctx.clip();
ctx.beginPath();
ctx.fillStyle = 'tomato';
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
ctx.restore();
.container {
  position: relative;
  height: 300px;
  width: 400px;
}
#torn-canvas {
  position: absolute;
  z-index: -1;
}
<div class='container'>
  <canvas id='torn-canvas' height='300px' width='300px'></canvas>Lorem ipsum dolor sit amet...</div>

We can even add an image as the background by first drawing the image on to the Canvas and then clipping it to shape.

var canvas = document.getElementById('torn-canvas');
var ctx = canvas.getContext('2d');
var lastX = 0,
  randX, randY, img = new Image();

ctx.save();
img.src = 'http://lorempixel.com/400/300/nature/4';
img.onload = function() {
  ctx.drawImage(img, 0, 0);
}
ctx.restore();
ctx.beginPath();
ctx.moveTo(0, 0);

randY = (Math.floor(Math.random() * 10) + 85) / 100 * canvas.height;
ctx.lineTo(0, randY);

while (lastX <= canvas.width) {
  randX = (Math.floor(Math.random() * 5)) / 100 * canvas.width;
  randY = (Math.floor(Math.random() * 10) + 85) / 100 * canvas.height;
  lastX = lastX + randX;
  ctx.lineTo(lastX, randY);
}
ctx.lineTo(canvas.width, 0);
ctx.closePath();
ctx.clip();
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.restore();
.container {
  position: relative;
  height: 300px;
  width: 400px;
  color: white;
}
#torn-canvas {
  position: absolute;
  z-index: -1;
}
<div class='container'>
  <canvas id='torn-canvas' height='300px' width='300px'></canvas>Lorem ipsum dolor sit amet...</div>

Solution 3:

I am sure this post is likely dead, but I am leaving this comment here in case anyone finds it helpful. I created a jagged effect with the following CSS:

clip-path: polygon(3% 0, 7% 1%, 11% 0%, 16% 2%, 20% 0, 23% 2%, 28% 2%, 32% 1%, 35% 1%, 39% 3%, 41% 1%, 45% 0%, 47% 2%, 50% 2%, 53% 0, 58% 2%, 60% 2%, 63% 1%, 65% 0%, 67% 2%, 69% 2%, 73% 1%, 76% 1%, 79% 0, 82% 1%, 85% 0, 87% 1%, 89% 0, 92% 1%, 96% 0, 98% 3%, 99% 3%, 99% 6%, 100% 11%, 98% 15%, 100% 21%, 99% 28%, 100% 32%, 99% 35%, 99% 40%, 100% 43%, 99% 48%, 100% 53%, 100% 57%, 99% 60%, 100% 64%, 100% 68%, 99% 72%, 100% 75%, 100% 79%, 99% 83%, 100% 86%, 100% 90%, 99% 94%, 99% 98%, 95% 99%, 92% 99%, 89% 100%, 86% 99%, 83% 100%, 77% 99%, 72% 100%, 66% 98%, 62% 100%, 59% 99%, 54% 99%, 49% 100%, 46% 98%, 43% 100%, 40% 98%, 38% 100%, 35% 99%, 31% 100%, 28% 99%, 25% 99%, 22% 100%, 19% 99%, 16% 100%, 13% 99%, 10% 99%, 7% 100%, 4% 99%, 2% 97%, 1% 97%, 0% 94%, 1% 89%, 0% 84%, 1% 81%, 0 76%, 0 71%, 1% 66%, 0% 64%, 0% 61%, 0% 59%, 1% 54%, 0% 49%, 1% 45%, 0% 40%, 1% 37%, 0% 34%, 1% 29%, 0% 23%, 2% 20%, 1% 17%, 1% 13%, 0 10%, 1% 6%, 1% 3%);

Now, I am sure this can be refined further to give a more convincing look, but I think this is a great way to give a rough shape to an image area.