Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
lefrancois committed Oct 12, 2021
1 parent 0d9a447 commit 5de5f1d
Show file tree
Hide file tree
Showing 15 changed files with 506 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.vscode
8 changes: 8 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
MIT License
Copyright (c) 2021 Michael Lefrancois

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Alpine Focal

Add focal point alignment of images to your Alpine 3.x components with a custom directive.

![GitHub](https://img.shields.io/github/license/lefrancois/alpine-focal?color=red&style=flat-square)
![Build size Brotli](https://img.badgesize.io/lefrancois/alpine-focal/master/dist/cdn.min.js.svg?compression=gzip&style=flat-square&color=green)
[![](https://data.jsdelivr.com/v1/package/npm/@lefrancois/alpine-focal/badge)](https://www.jsdelivr.com/package/npm/@lefrancois/alpine-focal)
[![](https://img.shields.io/discord/492260310414262274.svg?color=7289da&label=lefrancois&logo=discord&style=flat-square)](https://discord.gg/fnBQBdVdSZ)
[![](https://img.shields.io/twitter/follow/lefrancois?style=flat-square)](https://www.twitter.com/lefrancois)

> This package only supports Alpine v3.x.
## About

This plugin adds a new `x-focal` directive to Alpine that allows to easliy align your images to keep focus on the selected part of the image.

## Installation

### CDN

Include the following `<script>` tag in the `<head>` of your document, just before Alpine.

```html
<script
src="https://cdn.jsdelivr.net/npm/@lefrancois/alpine-focal@1/dist/cdn.min.js"
defer
></script>
```

### NPM

```bash
npm install @lefrancois/alpine-focal
```

Add the `x-focal` directive to your project by registering the plugin with Alpine.

```js
import Alpine from "alpinejs";
import Focal from "@lefrancois/alpine-focal";

Alpine.plugin(Focal);

window.Alpine = Alpine;
window.Alpine.start();
```

## Usage

Attach the `x-focal` directive on an image element and enter the point (unsing px or % informations) to keep in focus while containing the image.

```html
<div x-data>
<img
src="..."
width="400px"
height="600px"
x-focal="10% 80%"
>
</img>
</div>
```

Optinally you can use set the third parameter to true (`x-focal="100px 100px true"`) to get a preview image where you can set the focal point with an easy click on the image. Note: Just copy and paste the value to the `x-focal` attribute of the image element in production. ;) This is just a helper for your development.

## License

Copyright (c) 2021 Michael Lefrancois

Licensed under the MIT license, see [LICENSE.md](LICENSE.md) for details.
5 changes: 5 additions & 0 deletions builds/cdn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Focal from '../src/index'

document.addEventListener('alpine:initializing', () => {
window.Alpine.plugin(Focal)
})
3 changes: 3 additions & 0 deletions builds/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Focal from '../src/index'

export default Focal
52 changes: 52 additions & 0 deletions dist/cdn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
(() => {
// src/index.js
function src_default(Alpine) {
Alpine.directive("focal", (el, {expression}) => {
const parts = expression.split(" ");
let xOffset = parts[0] || "50%";
let yOffset = parts[1] || "50%";
let edit = parts[2] || false;
if (edit) {
el.style["object-fit"] = "";
el.onclick = (e) => {
const rect = el.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
const shiftCenterX = (rect.width / 2 - x * rect.width) / rect.width;
const shiftCenterY = (rect.height / 2 - y * rect.height) / rect.height;
xOffset = (x - shiftCenterX / 2) * 100;
yOffset = (y - shiftCenterY / 2) * 100;
if (xOffset > 100)
xOffset = 100;
if (xOffset < 0)
xOffset = 0;
if (yOffset > 100)
yOffset = 100;
if (yOffset < 0)
yOffset = 0;
xOffset = Math.round(xOffset);
yOffset = Math.round(yOffset);
const xPixelOffset = Math.round(xOffset / 100 * el.naturalWidth);
const yPixelOffset = Math.round(yOffset / 100 * el.naturalHeight);
el.style["object-fit"] = "cover";
el.style["object-position"] = `${xOffset}% ${yOffset}%`;
alert(`Use ${xOffset}% ${yOffset}% or ${xPixelOffset}px ${yPixelOffset}px`);
};
} else {
if (xOffset.endsWith("x")) {
xOffset = `${parseInt(xOffset) * 100 / el.naturalWidth}%`;
}
if (yOffset.endsWith("x")) {
yOffset = `${parseInt(yOffset) * 100 / el.naturalHeight}%`;
}
el.style["object-fit"] = "cover";
el.style["object-position"] = `${xOffset} ${yOffset}`;
}
});
}

// builds/cdn.js
document.addEventListener("alpine:initializing", () => {
window.Alpine.plugin(src_default);
});
})();
1 change: 1 addition & 0 deletions dist/cdn.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions dist/module.cjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};

// builds/module.js
__markAsModule(exports);
__export(exports, {
default: () => module_default
});

// src/index.js
function src_default(Alpine) {
Alpine.directive("focal", (el, {expression}) => {
const parts = expression.split(" ");
let xOffset = parts[0] || "50%";
let yOffset = parts[1] || "50%";
let edit = parts[2] || false;
if (edit) {
el.style["object-fit"] = "";
el.onclick = (e) => {
const rect = el.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
const shiftCenterX = (rect.width / 2 - x * rect.width) / rect.width;
const shiftCenterY = (rect.height / 2 - y * rect.height) / rect.height;
xOffset = (x - shiftCenterX / 2) * 100;
yOffset = (y - shiftCenterY / 2) * 100;
if (xOffset > 100)
xOffset = 100;
if (xOffset < 0)
xOffset = 0;
if (yOffset > 100)
yOffset = 100;
if (yOffset < 0)
yOffset = 0;
xOffset = Math.round(xOffset);
yOffset = Math.round(yOffset);
const xPixelOffset = Math.round(xOffset / 100 * el.naturalWidth);
const yPixelOffset = Math.round(yOffset / 100 * el.naturalHeight);
el.style["object-fit"] = "cover";
el.style["object-position"] = `${xOffset}% ${yOffset}%`;
alert(`Use ${xOffset}% ${yOffset}% or ${xPixelOffset}px ${yPixelOffset}px`);
};
} else {
if (xOffset.endsWith("x")) {
xOffset = `${parseInt(xOffset) * 100 / el.naturalWidth}%`;
}
if (yOffset.endsWith("x")) {
yOffset = `${parseInt(yOffset) * 100 / el.naturalHeight}%`;
}
el.style["object-fit"] = "cover";
el.style["object-position"] = `${xOffset} ${yOffset}`;
}
});
}

// builds/module.js
var module_default = src_default;
51 changes: 51 additions & 0 deletions dist/module.esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// src/index.js
function src_default(Alpine) {
Alpine.directive("focal", (el, {expression}) => {
const parts = expression.split(" ");
let xOffset = parts[0] || "50%";
let yOffset = parts[1] || "50%";
let edit = parts[2] || false;
if (edit) {
el.style["object-fit"] = "";
el.onclick = (e) => {
const rect = el.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
const shiftCenterX = (rect.width / 2 - x * rect.width) / rect.width;
const shiftCenterY = (rect.height / 2 - y * rect.height) / rect.height;
xOffset = (x - shiftCenterX / 2) * 100;
yOffset = (y - shiftCenterY / 2) * 100;
if (xOffset > 100)
xOffset = 100;
if (xOffset < 0)
xOffset = 0;
if (yOffset > 100)
yOffset = 100;
if (yOffset < 0)
yOffset = 0;
xOffset = Math.round(xOffset);
yOffset = Math.round(yOffset);
const xPixelOffset = Math.round(xOffset / 100 * el.naturalWidth);
const yPixelOffset = Math.round(yOffset / 100 * el.naturalHeight);
el.style["object-fit"] = "cover";
el.style["object-position"] = `${xOffset}% ${yOffset}%`;
alert(`Use ${xOffset}% ${yOffset}% or ${xPixelOffset}px ${yPixelOffset}px`);
};
} else {
if (xOffset.endsWith("x")) {
xOffset = `${parseInt(xOffset) * 100 / el.naturalWidth}%`;
}
if (yOffset.endsWith("x")) {
yOffset = `${parseInt(yOffset) * 100 / el.naturalHeight}%`;
}
el.style["object-fit"] = "cover";
el.style["object-position"] = `${xOffset} ${yOffset}`;
}
});
}

// builds/module.js
var module_default = src_default;
export {
module_default as default
};
25 changes: 25 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alpine.js Focal</title>
<script type="module">
import Focal from '../dist/module.esm.js'

document.addEventListener('alpine:initializing', () => {
window.Alpine.plugin(Focal)
})
</script>
<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
</head>
<body>
<img
src="https://images.unsplash.com/photo-1542114904-3bc059596c5f?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2070&q=80"
width="400px"
height="600px"
x-focal="1656px 345px"
x-data>
</img>
</body>
</html>
67 changes: 67 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@lefrancois/alpine-focal",
"description": "Add focal point image alignment to your Alpine 3.x components with a custom directive.",
"version": "1.0.0",
"author": {
"name": "Michael Lefrancois",
"email": "[email protected]",
"url": "https://www.lefrancois.de"
},
"repository": {
"type": "git",
"url": "https://github.com/lefrancois/alpine-focal"
},
"main": "dist/module.cjs.js",
"module": "dist/module.esm.js",
"devDependencies": {
"brotli-size": "^4.0.0",
"esbuild": "^0.8.39"
},
"scripts": {
"build": "node ./scripts/build.js",
"watch": "node ./scripts/build.js --watch"
}
}
Loading

0 comments on commit 5de5f1d

Please sign in to comment.