mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-07-01 21:42:45 +08:00
更改图标功能
This commit is contained in:
parent
5b9d739b9b
commit
0e5b382761
132
public/lib/picture-compressor.js
Normal file
132
public/lib/picture-compressor.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global = global || self, global.pictureCompress = factory());
|
||||||
|
}(this, function () { 'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将图片压缩为对应尺寸
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {String} options.img 图片的url或者base64数据
|
||||||
|
* @param {Number} options.width 目标图片的宽度
|
||||||
|
* @param {Number} options.height 目标图片的高度
|
||||||
|
* @param {Number} options.quality 生成目标图片质量
|
||||||
|
* @param {String} options.fit 图片压缩填充模式默认 scale:按比例缩放,可选 fill:按使用目标尺寸
|
||||||
|
* @param {String} options.type 图片压缩类型默认 jpg,可选 png
|
||||||
|
* @param {Number} options.rotate 图片旋转,由于手机拍照的角度和我们使用的头像不一致,需要旋转 默认0 仅支持 90 180 -90
|
||||||
|
* @returns {Promise} then {width,height,img}
|
||||||
|
*/
|
||||||
|
function pictureCompress(options) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
if (!options.img) {
|
||||||
|
reject(new Error('need img'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var imgSrc = options.img,
|
||||||
|
width = options.width || 640,
|
||||||
|
height = options.height || 640,
|
||||||
|
type = options.type || 'jpg',
|
||||||
|
quality = options.quality || 0.92,
|
||||||
|
fit = options.fit || 'scale',
|
||||||
|
rotate = options.rotate || 0;
|
||||||
|
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
reject(new Error('dist width or height need > 0'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/jpg|png|jpeg/.test(type)) {
|
||||||
|
reject(new Error('type need jpg or png!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotate !== 90 && rotate !== -90 && rotate !== 0 && rotate !== 180) {
|
||||||
|
reject(new Error('rotate mast be 0 90 -90 180!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var changeWidthAndHeight = rotate === 90 || rotate === -90;
|
||||||
|
var image = new Image();
|
||||||
|
image.src = imgSrc;
|
||||||
|
|
||||||
|
image.onload = function () {
|
||||||
|
var distSize = getDistSize({
|
||||||
|
width: changeWidthAndHeight ? this.naturalHeight : this.naturalWidth,
|
||||||
|
height: changeWidthAndHeight ? this.naturalWidth : this.naturalHeight
|
||||||
|
}, {
|
||||||
|
width: changeWidthAndHeight ? height : width,
|
||||||
|
height: changeWidthAndHeight ? width : height
|
||||||
|
}, fit);
|
||||||
|
var imgData = compress(this, distSize.width, distSize.height, type, quality, rotate);
|
||||||
|
resolve({
|
||||||
|
width: distSize.width,
|
||||||
|
height: distSize.height,
|
||||||
|
img: imgData
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
image.onerror = function (err) {
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 将图片转换为固定尺寸的
|
||||||
|
* @param {Image} img 图片数据
|
||||||
|
* @param {Number} width 转换之后的图片宽度
|
||||||
|
* @param {Number} height 转换之后的图片高度
|
||||||
|
* @param {String} type base64的图片类型 jpg png
|
||||||
|
* @param {Number} quality 转换之后的图片质量
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function compress(img, width, height, type, quality, rotate) {
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
var types = {
|
||||||
|
'jpg': 'image/jpeg',
|
||||||
|
'jpeg': 'image/jpeg',
|
||||||
|
'png': 'image/png'
|
||||||
|
};
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
if (rotate === 90) {
|
||||||
|
ctx.translate(width, 0);
|
||||||
|
ctx.rotate(90 * Math.PI / 180);
|
||||||
|
ctx.drawImage(img, 0, 0, height, width);
|
||||||
|
} else if (rotate === -90) {
|
||||||
|
ctx.translate(0, height);
|
||||||
|
ctx.rotate(-90 * Math.PI / 180);
|
||||||
|
ctx.drawImage(img, 0, 0, height, width);
|
||||||
|
} else if (rotate === 180) {
|
||||||
|
ctx.translate(width, height);
|
||||||
|
ctx.rotate(180 * Math.PI / 180);
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.toDataURL(types[type], quality);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 选择源尺寸与目标尺寸比例中较小的那个,保证图片可以完全显示
|
||||||
|
* 最大值不超过1,如果图片源尺寸小于目标尺寸,则不做处理,返回图片原尺寸
|
||||||
|
* @param {Object} source 源图片的宽高
|
||||||
|
* @param {Object} dist 目标图片的宽高
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function getDistSize(source, dist, fit) {
|
||||||
|
if (fit === 'fill') return dist;
|
||||||
|
var scale = Math.min(dist.width / source.width, dist.height / source.height, 1);
|
||||||
|
return {
|
||||||
|
width: Math.round(source.width * scale),
|
||||||
|
height: Math.round(source.height * scale)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return pictureCompress;
|
||||||
|
|
||||||
|
}));
|
@ -11,6 +11,7 @@ const path = require("path")
|
|||||||
const util = require("util")
|
const util = require("util")
|
||||||
const PinyinMatch = require('pinyin-match');
|
const PinyinMatch = require('pinyin-match');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
const pictureCompress = require("./lib/picture-compressor")
|
||||||
|
|
||||||
_ = require("lodash")
|
_ = require("lodash")
|
||||||
window.pinYinMatch = PinyinMatch
|
window.pinYinMatch = PinyinMatch
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
/>
|
/>
|
||||||
<q-img
|
<q-img
|
||||||
class="commandLogo"
|
class="commandLogo"
|
||||||
|
@click="showIconPicker"
|
||||||
width="64px"
|
width="64px"
|
||||||
:src="currentCommand.features.icon"
|
:src="currentCommand.features.icon"
|
||||||
/>
|
/>
|
||||||
@ -230,6 +231,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<iconPicker
|
||||||
|
@iconChanged="(dataUrl) => (currentCommand.features.icon = dataUrl)"
|
||||||
|
ref="icon"
|
||||||
|
/>
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -237,9 +242,11 @@
|
|||||||
import commandTypes from "../js/options/commandTypes.js";
|
import commandTypes from "../js/options/commandTypes.js";
|
||||||
import outputTypes from "../js/options/outputTypes.js";
|
import outputTypes from "../js/options/outputTypes.js";
|
||||||
import specialVars from "../js/options/specialVars.js";
|
import specialVars from "../js/options/specialVars.js";
|
||||||
|
import iconPicker from "components/IconPicker.vue";
|
||||||
let commandTypesOptions = Object.values(commandTypes);
|
let commandTypesOptions = Object.values(commandTypes);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { iconPicker },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentCommand: {
|
currentCommand: {
|
||||||
@ -380,6 +387,10 @@ export default {
|
|||||||
}
|
}
|
||||||
return updateData;
|
return updateData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showIconPicker() {
|
||||||
|
this.$refs.icon.showIconPicker = true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -392,6 +403,6 @@ export default {
|
|||||||
}
|
}
|
||||||
.commandLogo:hover {
|
.commandLogo:hover {
|
||||||
transition: 10s;
|
transition: 10s;
|
||||||
transform: rotate(360deg);
|
transform: rotate(1080deg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
149
src/components/IconPicker.vue
Normal file
149
src/components/IconPicker.vue
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<q-dialog v-model="showIconPicker" class="q-gutter" position="left">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section class="text-h5 text-center">更改图标</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-select
|
||||||
|
ref="icon8"
|
||||||
|
v-model="icon8sIcon"
|
||||||
|
:options="options"
|
||||||
|
label="搜索ICON8S"
|
||||||
|
options-dense
|
||||||
|
use-input
|
||||||
|
:loading="loading"
|
||||||
|
input-debounce="1000"
|
||||||
|
@input-value="searchIcon8s"
|
||||||
|
@update:model-value="getIcon8sIcon"
|
||||||
|
transition-show="jump-down"
|
||||||
|
transition-hide="jump-up"
|
||||||
|
virtual-scroll-slice-size="30"
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
>
|
||||||
|
<template v-slot:selected>
|
||||||
|
{{ icon8sIcon?.name || "" }}
|
||||||
|
</template>
|
||||||
|
<template v-slot:prepend> <q-icon name="image" /> </template>
|
||||||
|
<template v-slot:option="scope">
|
||||||
|
<q-item v-bind="scope.itemProps">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-img
|
||||||
|
:src="`https://img.icons8.com/color/1x/${scope.opt.commonName}.png`"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label v-html="scope.opt.name" />
|
||||||
|
</q-item-section>
|
||||||
|
</q-item> </template
|
||||||
|
></q-select>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-file
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
v-model="localIconFile"
|
||||||
|
@update:model-value="getLocalIcon"
|
||||||
|
accept="image/*"
|
||||||
|
label="选择本地图片"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-icon name="folder" />
|
||||||
|
</template>
|
||||||
|
</q-file>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-input
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
v-model="netWorkIconUrl"
|
||||||
|
@blur="getRemoteIcon"
|
||||||
|
type="text"
|
||||||
|
label="网络图片地址"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend> <q-icon name="cloud" /> </template
|
||||||
|
></q-input>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn flat color="primary" @click="showIconPicker = false"
|
||||||
|
>确定</q-btn
|
||||||
|
>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showIconPicker: false,
|
||||||
|
localIconFile: ref(null),
|
||||||
|
icon8sIcon: ref(null),
|
||||||
|
netWorkIconUrl: "",
|
||||||
|
options: ref(null),
|
||||||
|
loading: false,
|
||||||
|
icon8: {
|
||||||
|
platform: "color",
|
||||||
|
amount: "300",
|
||||||
|
baseUrl: "https://search.icons8.com/api/iconsets/v5/search",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setIcon(dataUrl) {
|
||||||
|
this.$emit("iconChanged", dataUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
searchIcon8s(val) {
|
||||||
|
if (!val) return;
|
||||||
|
this.loading = true;
|
||||||
|
let language = /[\u4e00-\u9fa5]/.test(val) ? "zh" : "en";
|
||||||
|
let apiUrl = `${this.icon8.baseUrl}?term=${val}&amount=${this.icon8.amount}&offset=0&platform=${this.icon8.platform}&language=${language}`;
|
||||||
|
fetch(apiUrl)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
this.options = res.icons;
|
||||||
|
this.$refs.icon8.showPopup();
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getIcon8sIcon() {
|
||||||
|
let imgUrl = `https://img.icons8.com/color/2x/${this.icon8sIcon.commonName}.png`;
|
||||||
|
this.getRemoteIcon(imgUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
getLocalIcon() {
|
||||||
|
window.getBase64Ico(this.localIconFile.path).then((dataUrl) => {
|
||||||
|
dataUrl && this.setIcon(dataUrl);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getRemoteIcon(url = this.netWorkIconUrl) {
|
||||||
|
this.getImg(url, (dataUrl) => {
|
||||||
|
dataUrl && this.setIcon(dataUrl);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getImg(imgUrl, callback) {
|
||||||
|
let imgInfo = window.getFileInfo({
|
||||||
|
type: "file",
|
||||||
|
argvs: imgUrl,
|
||||||
|
readfile: false,
|
||||||
|
});
|
||||||
|
let imgPath = window.getQuickcommandTempFile(imgInfo.ext);
|
||||||
|
quickcommand
|
||||||
|
.downloadFile(imgUrl, imgPath)
|
||||||
|
.then(() => {
|
||||||
|
window.getBase64Ico(imgPath).then((src) => {
|
||||||
|
callback(src);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
quickcommand.showMessageBox("图片地址有误!", "error");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user