Skip to content

Commit

Permalink
feat: add srcset generation (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
sherwinski authored Sep 27, 2019
1 parent eee6c4f commit f66ae42
Show file tree
Hide file tree
Showing 3 changed files with 680 additions and 1 deletion.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,51 @@ using Imgix;
var builder = new UrlBuilder("domain.imgix.net", includeLibraryParam: false);
```

Srcset Generation
-----------

The imgix-csharp library allows for generation of custom `srcset` attributes, which can be invoked through `BuildSrcSet()`. By default, the `srcset` generated will allow for responsive size switching by building a list of image-width mappings.

```csharp
var builder = new UrlBuilder("domain.imgix.net", "my-token", false, true);
String srcset = builder.BuildSrcSet("bridge.png");
Debug.Print(srcset);
```

Will produce the following attribute value, which can then be served to the client:

```html
https://domain.imgix.net/bridge.png?w=100&s=494158d968e94ac8e83772ada9a83ad1 100w,
https://domain.imgix.net/bridge.png?w=116&s=6a22236e189b6a9548b531330647ffa7 116w,
https://domain.imgix.net/bridge.png?w=134&s=cbf91f556dd67c0b9e26cb9784a83794 134w,
...
https://domain.imgix.net/bridge.png?w=7400&s=503e3ba04588f1c301863c9a5d84fe91 7400w,
https://domain.imgix.net/bridge.png?w=8192&s=152551ce4ec155f7a03f60f762a1ca33 8192w
```

In cases where enough information is provided about an image's dimensions, `BuildSrcSet()` will instead build a `srcset` that will allow for an image to be served at different resolutions. The parameters taken into consideration when determining if an image is fixed-width are `w` (width), `h` (height), and `ar` (aspect ratio). By invoking `BuildSrcSet()` with either a width **or** the height and aspect ratio (along with `fit=crop`, typically) provided, a different `srcset` will be generated for a fixed-size image instead.

```csharp
var builder = new UrlBuilder("domain.imgix.net", "my-token", false, true);
var parameters = new Dictionary<String, String>();
parameters["h"] = "200";
parameters["ar"] = "3:2";
parameters["fit"] = "crop";
String srcset = builder.BuildSrcSet("bridge.png", parameters);
Console.WriteLine(srcset);
```

Will produce the following attribute value:

```html
https://domain.imgix.net/bridge.png?h=200&ar=3%3A2&fit=crop&dpr=1&s=f39a78a6a2f245a70ba6aac910088435 1x,
https://domain.imgix.net/bridge.png?h=200&ar=3%3A2&fit=crop&dpr=2&s=d5dfd75bd777283d82975ab18a3091ff 2x,
https://domain.imgix.net/bridge.png?h=200&ar=3%3A2&fit=crop&dpr=3&s=8f25811130e3573530754c52f86a851d 3x,
https://domain.imgix.net/bridge.png?h=200&ar=3%3A2&fit=crop&dpr=4&s=ec348479a843a688c2ef9be487ea9be8 4x,
https://domain.imgix.net/bridge.png?h=200&ar=3%3A2&fit=crop&dpr=5&s=ce70bbfd682e683497f1afa6118ae2e3 5x
```

For more information to better understand `srcset`, we highly recommend [Eric Portis' "Srcset and sizes" article](https://ericportis.com/posts/2014/srcset-sizes/) which goes into depth about the subject.

## Code of Conduct
Users contributing to or participating in the development of this project are subject to the terms of imgix's [Code of Conduct](https://github.com/imgix/code-of-conduct).
72 changes: 71 additions & 1 deletion src/Imgix/UrlBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ public class UrlBuilder

private String _signKey;
public String SignKey { set { _signKey = value; } }

private String Domain;

private static readonly List<int> SRCSET_TARGET_WIDTHS = GenerateTargetWidths();

public UrlBuilder(String domain,
String signKey = null,
Boolean includeLibraryParam = true,
Expand Down Expand Up @@ -59,6 +60,29 @@ public String BuildUrl(String path, Dictionary<String, String> parameters)
return GenerateUrl(Domain, path, parameters);
}

public String BuildSrcSet(String path)
{
return BuildSrcSet(path, new Dictionary<string, string>());
}

public String BuildSrcSet(String path, Dictionary<String, String> parameters)
{
String srcset;
parameters.TryGetValue("w", out String width);
parameters.TryGetValue("h", out String height);
parameters.TryGetValue("ar", out String aspectRatio);

if ((width != null) || (height != null && aspectRatio != null))
{
srcset = GenerateSrcSetDPR(Domain, path, parameters);
}
else
{
srcset = GenerateSrcSetPairs(Domain, path, parameters);
}

return srcset;
}

private String GenerateUrl(String domain, String path, Dictionary<String, String> parameters)
{
Expand All @@ -77,6 +101,52 @@ private String GenerateUrl(String domain, String path, Dictionary<String, String
return String.Format("{0}://{1}/{2}{3}", scheme, domain, path, localParams.Any() ? "?" + GenerateUrlStringFromDict(localParams) : String.Empty);
}

private String GenerateSrcSetDPR(String domain, String path, Dictionary<String, String> parameters)
{
String srcset = "";
int[] targetRatios = { 1, 2, 3, 4, 5 };

foreach(int ratio in targetRatios)
{
parameters["dpr"] = ratio.ToString();
parameters.Remove("ixlib");
srcset += BuildUrl(path, parameters) + " " + ratio.ToString()+ "x,\n";
}

return srcset.Substring(0, srcset.Length - 2);
}

private String GenerateSrcSetPairs(String domain, String path, Dictionary<String, String> parameters)
{
String srcset = "";

foreach(int width in SRCSET_TARGET_WIDTHS)
{
parameters["w"] = width.ToString();
parameters.Remove("ixlib");
srcset += BuildUrl(path, parameters) + " " + width + "w,\n";
}

return srcset.Substring(0, srcset.Length - 2);
}

private static List<int> GenerateTargetWidths()
{
List<int> resolutions = new List<int>();
int MAX_SIZE = 8192, roundedPrev;
double SRCSET_PERCENT_INCREMENT = 8;
double prev = 100;

while (prev < MAX_SIZE)
{
roundedPrev = (int)(2 * Math.Round(prev / 2));
resolutions.Add(roundedPrev);
prev *= 1 + (SRCSET_PERCENT_INCREMENT / 100) * 2;
}
resolutions.Add(MAX_SIZE);

return resolutions;
}

private String HashString(String input)
{
Expand Down
Loading

0 comments on commit f66ae42

Please sign in to comment.