1
0
mirror of https://github.com/SunnyQjm/taro-cropper.git synced 2026-06-03 08:16:46 +08:00

add: 基本实现图片的移动和缩放功能

This commit is contained in:
SunnyQjm
2019-08-18 08:46:31 +08:00
parent 5c8290d5c7
commit 756a0d4899
11 changed files with 569 additions and 110 deletions
-1
View File
@@ -1,7 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
+86
View File
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings" wasCopied="true">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="GITLAB_EXT" value="false" />
<option name="GITLAB_MATH_EXT" value="false" />
<option name="GITLAB_MERMAID_EXT" value="false" />
<option name="HEADER_ID_NON_ASCII_TO_LOWERCASE" value="false" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="MACROS_EXT" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="PLANTUML_FENCED_CODE" value="false" />
<option name="PUML_FENCED_CODE" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0" mathConversion="-1">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>
-83
View File
@@ -4,87 +4,4 @@
<option name="languageLevel" value="ES6" />
</component>
<component name="MarkdownNavigator.ProfileManager" plain-text-search-scope="Project Files" />
<component name="MarkdownProjectSettings" wasCopied="true">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="GITLAB_EXT" value="false" />
<option name="GITLAB_MATH_EXT" value="false" />
<option name="GITLAB_MERMAID_EXT" value="false" />
<option name="HEADER_ID_NON_ASCII_TO_LOWERCASE" value="false" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="MACROS_EXT" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="PLANTUML_FENCED_CODE" value="false" />
<option name="PUML_FENCED_CODE" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0" mathConversion="-1">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/taro-cropper.iml" filepath="$PROJECT_DIR$/.idea/taro-cropper.iml" />
</modules>
</component>
</project>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Generated
+1
View File
@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/src/components/image-cropper" vcs="Git" />
</component>
</project>
+4 -3
View File
@@ -29,6 +29,9 @@
"author": "",
"license": "MIT",
"dependencies": {
"we-cropper": "^1.3.7"
},
"devDependencies": {
"@tarojs/components": "1.3.13",
"@tarojs/router": "1.3.13",
"@tarojs/taro": "1.3.13",
@@ -40,9 +43,7 @@
"@tarojs/taro-tt": "1.3.13",
"@tarojs/taro-weapp": "1.3.13",
"nervjs": "^1.4.0",
"nerv-devtools": "^1.4.0"
},
"devDependencies": {
"nerv-devtools": "^1.4.0",
"@types/react": "^16.4.6",
"@types/webpack-env": "^1.13.6",
"@tarojs/plugin-babel": "1.3.13",
+6 -3
View File
@@ -1,13 +1,16 @@
{
"miniprogramRoot": "./dist",
"miniprogramRoot": "dist/",
"projectname": "taro-cropper",
"description": "we-cropper在Taro框架下的封装,开箱即用",
"appid": "touristappid",
"appid": "wx47e69a70a727989c",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram"
"compileType": "miniprogram",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {}
}
+3
View File
@@ -0,0 +1,3 @@
.taro-cropper-component {
position: relative;
}
+397
View File
@@ -0,0 +1,397 @@
import Taro, {CanvasContext, getImageInfo, getSystemInfoSync} from '@tarojs/taro';
import {Canvas, View} from '@tarojs/components';
import './index.scss';
import {ITouch, ITouchEvent} from "@tarojs/components/types/common";
interface TaroCropperComponentProps {
bgCanvasId: string, // 背景画布id
cropperCanvasId: string, // 裁剪框画布id
width: number, // 组件宽度
height: number, // 组件高度 (要求背景高度大于宽度)
cropperWidth: number, // 裁剪框宽度
cropperHeight: number, // 裁剪框高度
src: string,
}
interface TaroCropperComponentState {
scale: number,
}
class TaroCropperComponent extends Taro.PureComponent<TaroCropperComponentProps, TaroCropperComponentState> {
static defaultProps = {
width: 750,
height: 1200,
cropperWidth: 300,
cropperHeight: 300,
bgCanvasId: 'TaroBgCanvasId',
cropperCanvasId: 'TaroCropperCanvasId',
src: '',
};
systemInfo: getSystemInfoSync.Return;
bgCanvasContext: CanvasContext;
cropperCanvasContext: CanvasContext;
constructor(props) {
super(props);
this.update = this.update.bind(this);
this.handleOnTouchMove = this.handleOnTouchMove.bind(this);
this.handleOnTouchStart = this.handleOnTouchStart.bind(this);
this.handleOnTouchEnd = this.handleOnTouchEnd.bind(this);
this._drawCropperCorner = this._drawCropperCorner.bind(this);
this._drawCropperContent = this._drawCropperContent.bind(this);
this.systemInfo = Taro.getSystemInfoSync();
this.state = {
scale: 1,
}
}
imageLeft: number = 0;
imageTop: number = 0;
imageLeftOrigin: number = 0;
imageTopOrigin: number = 0;
width: number = 0;
height: number = 0;
cropperWidth: number = 0;
cropperHeight: number = 0;
imageInfo: getImageInfo.Promised;
realImageWidth: number = 0;
realImageHeight: number = 0;
scaleImageWidth: number = 0;
scaleImageHeight: number = 0;
/**
* 根据props更新长等信息
*/
updateInfo(props: TaroCropperComponentProps) {
const {
width,
height,
cropperWidth,
cropperHeight,
src
} = props;
this.width = this._getRealPx(width);
this.height = this._getRealPx(height);
this.cropperWidth = this._getRealPx(cropperWidth);
this.cropperHeight = this._getRealPx(cropperHeight);
if (!src)
return Promise.reject();
return Taro.getImageInfo({
src: src
})
.then((res: getImageInfo.Promised) => {
this.imageInfo = res;
const imageWidth = res.width;
const imageHeight = res.height;
if (imageWidth / imageHeight < this.cropperWidth / this.cropperHeight) { // 宽度充满
this.scaleImageWidth = this.realImageWidth = this.cropperWidth;
this.scaleImageHeight = this.realImageHeight = this.realImageWidth * imageHeight / imageWidth;
this.imageLeftOrigin = this.imageLeft = (this.width - this.cropperWidth) / 2;
this.imageTopOrigin = this.imageTop = (this.height - this.realImageHeight) / 2;
} else {
this.scaleImageHeight = this.realImageHeight = this.cropperHeight;
this.scaleImageWidth = this.realImageWidth = this.realImageHeight * imageWidth / imageHeight;
this.imageLeftOrigin = this.imageLeft = (this.width - this.realImageWidth) / 2;
this.imageTopOrigin = this.imageTop = (this.height - this.cropperHeight) / 2;
}
return Promise.resolve()
})
}
componentDidMount(): void {
const {
cropperCanvasId,
bgCanvasId
} = this.props;
this.bgCanvasContext = Taro.createCanvasContext(bgCanvasId, this);
this.cropperCanvasContext = Taro.createCanvasContext(cropperCanvasId, this);
this.updateInfo(this.props)
.then(() => {
this.update();
})
.catch(() => {
this.update();
});
}
/**
* 单位转换
* @param value
* @private
*/
_getRealPx(value: number) {
return value / 750 * this.systemInfo.screenWidth;
}
/**
* 绘制裁剪框的四个角
* @private
*/
_drawCropperCorner() {
const cropperStartX = (this.width - this.cropperWidth) / 2;
const cropperStartY = (this.height - this.cropperHeight) / 2;
const lineWidth = 2;
const lineLength = 10;
this.cropperCanvasContext.beginPath();
this.cropperCanvasContext.setStrokeStyle('#0f0');
this.cropperCanvasContext.setLineWidth(lineWidth);
// 左上角
this.cropperCanvasContext.moveTo(cropperStartX, cropperStartY);
this.cropperCanvasContext.lineTo(cropperStartX + lineLength, cropperStartY);
this.cropperCanvasContext.moveTo(cropperStartX, cropperStartY - lineWidth / 2);
this.cropperCanvasContext.lineTo(cropperStartX, cropperStartY + lineLength);
// 右上角
this.cropperCanvasContext.moveTo(cropperStartX + this.cropperWidth, cropperStartY);
this.cropperCanvasContext.lineTo(cropperStartX + this.cropperWidth - lineLength, cropperStartY);
this.cropperCanvasContext.moveTo(cropperStartX + this.cropperWidth, cropperStartY - lineWidth / 2);
this.cropperCanvasContext.lineTo(cropperStartX + this.cropperWidth, cropperStartY + lineLength);
// 左下角
this.cropperCanvasContext.moveTo(cropperStartX, cropperStartY + this.cropperHeight);
this.cropperCanvasContext.lineTo(cropperStartX + lineLength, cropperStartY + this.cropperHeight);
this.cropperCanvasContext.moveTo(cropperStartX, cropperStartY + this.cropperHeight + lineWidth / 2);
this.cropperCanvasContext.lineTo(cropperStartX, cropperStartY + this.cropperHeight - lineLength);
// 右下角
this.cropperCanvasContext.moveTo(cropperStartX + this.cropperWidth, cropperStartY + this.cropperHeight);
this.cropperCanvasContext.lineTo(cropperStartX + this.cropperWidth - lineLength, cropperStartY + this.cropperHeight);
this.cropperCanvasContext.moveTo(cropperStartX + this.cropperWidth, cropperStartY + this.cropperHeight + lineWidth / 2);
this.cropperCanvasContext.lineTo(cropperStartX + this.cropperWidth, cropperStartY + this.cropperHeight - lineLength);
this.cropperCanvasContext.closePath();
this.cropperCanvasContext.stroke();
}
/**
* 绘制裁剪框区域的图片
* @param props
* @param src 待绘制的图片路径
* @param deviationX 图片绘制x向偏移
* @param deviationY 图片绘制y向偏移
* @param imageWidth 图片的原始宽度
* @param imageHeight 图片的原始高度
* @param drawWidth 图片的绘制宽度
* @param drawHeight 图片的绘制高度
* @param reverse
* @private
*/
_drawCropperContent(
// props: TaroCropperComponentProps,
src: string,
deviationX: number,
deviationY: number,
imageWidth: number,
imageHeight: number,
drawWidth: number,
drawHeight: number) {
const cropperStartX = (this.width - this.cropperWidth) / 2;
const cropperStartY = (this.height - this.cropperHeight) / 2;
const cropperImageX = (cropperStartX - deviationX) / drawWidth * imageWidth;
const cropperImageY = (cropperStartY - deviationY) / drawHeight * imageHeight;
const cropperImageWidth = this.cropperWidth / drawWidth * imageWidth;
const cropperImageHeight = this.cropperHeight / drawHeight * imageHeight;
// 绘制裁剪框内裁剪的图片
this.cropperCanvasContext.drawImage(src, cropperImageX, cropperImageY, cropperImageWidth, cropperImageHeight,
cropperStartX, cropperStartY, this.cropperWidth, this.cropperHeight);
this._drawCropperCorner();
}
update() {
if (!this.imageInfo) { // 图片资源无效则不执行更新操作
this._drawCropperCorner();
this.cropperCanvasContext.draw();
return;
}
this.cropperCanvasContext.drawImage(this.imageInfo.path, 0, 0, this.imageInfo.width, this.imageInfo.height,
this.imageLeft, this.imageTop, this.scaleImageWidth, this.scaleImageHeight);
// 绘制半透明层
this.cropperCanvasContext.beginPath();
this.cropperCanvasContext.setFillStyle('rgba(0, 0, 0, 0.3)');
this.cropperCanvasContext.fillRect(0, 0, this.width, this.height);
this.cropperCanvasContext.fill();
// 绘制裁剪框内部的区域
this._drawCropperContent(this.imageInfo.path, this.imageLeft, this.imageTop,
this.imageInfo.width, this.imageInfo.height, this.scaleImageWidth, this.scaleImageHeight);
this.cropperCanvasContext.draw(false);
}
/**
* 图片资源有更新则重新绘制
* @param nextProps
* @param nextContext
*/
componentWillReceiveProps(nextProps: Readonly<TaroCropperComponentProps>, nextContext: any): void {
if (JSON.stringify(nextProps) != JSON.stringify(this.props)) {
this.updateInfo(nextProps)
.then(() => {
this.update();
});
}
return super.componentWillReceiveProps && super.componentWillReceiveProps(nextProps, nextContext);
}
/**
* 图片移动边界检测
* @param imageLeft
* @param imageRight
* @private
*/
_outsideBound(imageLeft: number, imageTop: number) {
this.imageLeft =
imageLeft > (this.width - this.cropperWidth) / 2
?
(this.width - this.cropperWidth) / 2
:
(
(imageLeft + this.scaleImageWidth) >= (this.width + this.cropperWidth) / 2
?
imageLeft
:
(this.width + this.cropperWidth) / 2 - this.scaleImageWidth
);
this.imageTop =
imageTop > (this.height - this.cropperHeight) / 2
?
(this.height - this.cropperHeight) / 2
:
(
(imageTop + this.scaleImageHeight) >= (this.height + this.cropperHeight) / 2
?
imageTop
:
(this.height + this.cropperHeight) / 2 - this.scaleImageHeight
)
}
touch0X = 0;
touch0Y = 0;
oldDistance = 0;
oldScale = 1;
newScale = 1;
lastScaleImageWidth = 0;
lastScaleImageHeight = 0;
_oneTouchStart(touch: ITouch) {
this.touch0X = touch.x;
this.touch0Y = touch.y;
}
_twoTouchStart(touch0: ITouch, touch1: ITouch) {
const xMove = touch1.x - touch0.x;
const yMove = touch1.y - touch0.y;
this.lastScaleImageWidth = this.scaleImageWidth;
this.lastScaleImageHeight = this.scaleImageHeight;
// 计算得到初始时两指的距离
this.oldDistance = Math.sqrt(xMove * xMove + yMove * yMove);
}
_oneTouchMove(touch: ITouch) {
const xMove = touch.x - this.touch0X;
const yMove = touch.y - this.touch0Y;
this._outsideBound(this.imageLeftOrigin + xMove, this.imageTopOrigin + yMove);
this.update();
}
_getNewScale(oldScale: number, oldDistance: number, touch0: ITouch, touch1: ITouch) {
const xMove = touch1.x - touch0.x;
const yMove = touch1.y - touch0.y;
const newDistance = Math.sqrt(xMove * xMove + yMove * yMove);
return oldScale + 0.02 * (newDistance - oldDistance);
}
_twoTouchMove(touch0: ITouch, touch1: ITouch) {
const oldScale = this.oldScale;
const oldDistance = this.oldDistance;
this.newScale = this._getNewScale(oldScale, oldDistance, touch0, touch1);
// 限制缩放
this.newScale <= 1 && (this.newScale = 1);
this.newScale > 3 && (this.newScale = 3);
this.scaleImageWidth = this.realImageWidth * this.newScale;
this.scaleImageHeight = this.realImageHeight * this.newScale;
const imageLeft = this.imageLeftOrigin - (this.scaleImageWidth - this.lastScaleImageWidth) / 2;
const imageTop = this.imageTopOrigin - (this.scaleImageHeight - this.lastScaleImageHeight) / 2;
this._outsideBound(imageLeft, imageTop);
this.update();
}
handleOnTouchEnd() {
this.oldScale = this.newScale;
this.imageLeftOrigin = this.imageLeft;
this.imageTopOrigin = this.imageTop
}
handleOnTouchStart(e: ITouchEvent) {
const {
src
} = this.props;
if (!src)
return;
const touch0 = e.touches[0];
const touch1 = e.touches[1];
// 计算第一个触摸点的位置,并参照改点进行缩放
this._oneTouchStart(touch0);
// 两指手势触发
if (e.touches.length >= 2) {
this._twoTouchStart(touch0, touch1);
}
}
handleOnTouchMove(e: ITouchEvent) {
const {
src
} = this.props;
if (!src)
return;
// 单指手势触发
if (e.touches.length === 1) {
this._oneTouchMove(e.touches[0]);
} else if (e.touches.length >= 2) {// 双指手势触发
this._twoTouchMove(e.touches[0], e.touches[1]);
}
}
render(): any {
const {
width,
height,
cropperCanvasId,
} = this.props;
return (
<View className='taro-cropper-component'>
<Canvas
onTouchStart={this.handleOnTouchStart}
onTouchMove={this.handleOnTouchMove}
onTouchEnd={this.handleOnTouchEnd}
canvasId={cropperCanvasId}
style={{
width: `${width / 750 * this.systemInfo.screenWidth}px`,
height: `${height / 750 * this.systemInfo.screenWidth}px`,
background: 'rgba(0, 0, 0, 0.8)',
}}
disableScroll
/>
</View>
);
}
}
export default TaroCropperComponent;
+41 -9
View File
@@ -1,8 +1,17 @@
import Taro, {Component, Config} from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import {View, Button} from '@tarojs/components'
import './index.scss'
import TaroCropper from '../../components/taro-cropper';
export default class Index extends Component {
interface IndexProps {
}
interface IndexState {
src: string,
}
export default class Index extends Component<IndexProps, IndexState> {
/**
* 指定config的类型声明为: Taro.Config
@@ -12,23 +21,46 @@ export default class Index extends Component {
* 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型
*/
config: Config = {
navigationBarTitleText: '首页'
navigationBarTitleText: '首页',
};
constructor(props) {
super(props);
this.state = {
src: ''
}
}
componentWillMount () { }
componentWillMount() {
}
componentDidMount () { }
componentDidMount() {
}
componentWillUnmount () { }
componentWillUnmount() {
}
componentDidShow () { }
componentDidShow() {
}
componentDidHide () { }
componentDidHide() {
}
render() {
const {
src
} = this.state;
return (
<View className='index'>
<Text>Hello world!</Text>
<TaroCropper height={1000} src={src} cropperWidth={400} cropperHeight={400}/>
<Button onClick={() => {
Taro.chooseImage()
.then(res => {
this.setState({
src: res.tempFilePaths[0]
});
})
}}></Button>
</View>
)
}